@switchbot/homebridge-switchbot 5.0.0-beta.108 → 5.0.0-beta.109

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.
Files changed (148) hide show
  1. package/TODO.md +564 -0
  2. package/config.schema.json +100 -25
  3. package/dist/device-types.d.ts +15 -0
  4. package/dist/device-types.d.ts.map +1 -0
  5. package/dist/device-types.js +145 -0
  6. package/dist/device-types.js.map +1 -0
  7. package/dist/deviceFactory.d.ts +1 -1
  8. package/dist/deviceFactory.d.ts.map +1 -1
  9. package/dist/deviceFactory.js +55 -17
  10. package/dist/deviceFactory.js.map +1 -1
  11. package/dist/devices/genericDevice.d.ts +1 -1
  12. package/dist/devices/genericDevice.d.ts.map +1 -1
  13. package/dist/devices/genericDevice.js +4 -2
  14. package/dist/devices/genericDevice.js.map +1 -1
  15. package/dist/homebridge-ui/endpoints/config.d.ts +6 -0
  16. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
  17. package/dist/homebridge-ui/endpoints/config.js +73 -0
  18. package/dist/homebridge-ui/endpoints/config.js.map +1 -0
  19. package/dist/homebridge-ui/endpoints/credentials.d.ts +6 -0
  20. package/dist/homebridge-ui/endpoints/credentials.d.ts.map +1 -0
  21. package/dist/homebridge-ui/endpoints/credentials.js +65 -0
  22. package/dist/homebridge-ui/endpoints/credentials.js.map +1 -0
  23. package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
  24. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
  25. package/dist/homebridge-ui/endpoints/devices.js +285 -0
  26. package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
  27. package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
  28. package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
  29. package/dist/homebridge-ui/endpoints/discovery.js +171 -0
  30. package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
  31. package/dist/homebridge-ui/public/index.html +144 -624
  32. package/dist/homebridge-ui/public/js/api.d.ts +15 -0
  33. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
  34. package/dist/homebridge-ui/public/js/api.js +164 -0
  35. package/dist/homebridge-ui/public/js/api.js.map +1 -0
  36. package/dist/homebridge-ui/public/js/api.ts +189 -0
  37. package/dist/homebridge-ui/public/js/app.d.ts +2 -0
  38. package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
  39. package/dist/homebridge-ui/public/js/app.js +1069 -0
  40. package/dist/homebridge-ui/public/js/app.js.map +7 -0
  41. package/dist/homebridge-ui/public/js/app.ts +21 -0
  42. package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
  43. package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
  44. package/dist/homebridge-ui/public/js/constants.js +2 -0
  45. package/dist/homebridge-ui/public/js/constants.js.map +1 -0
  46. package/dist/homebridge-ui/public/js/constants.ts +1 -0
  47. package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
  48. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
  49. package/dist/homebridge-ui/public/js/credentials.js +96 -0
  50. package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
  51. package/dist/homebridge-ui/public/js/credentials.ts +100 -0
  52. package/dist/homebridge-ui/public/js/devices-delete.d.ts +2 -0
  53. package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
  54. package/dist/homebridge-ui/public/js/devices-delete.js +99 -0
  55. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
  56. package/dist/homebridge-ui/public/js/devices-delete.ts +113 -0
  57. package/dist/homebridge-ui/public/js/devices.d.ts +8 -0
  58. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
  59. package/dist/homebridge-ui/public/js/devices.js +71 -0
  60. package/dist/homebridge-ui/public/js/devices.js.map +1 -0
  61. package/dist/homebridge-ui/public/js/devices.ts +78 -0
  62. package/dist/homebridge-ui/public/js/discovery.d.ts +3 -0
  63. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
  64. package/dist/homebridge-ui/public/js/discovery.js +107 -0
  65. package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
  66. package/dist/homebridge-ui/public/js/discovery.ts +123 -0
  67. package/dist/homebridge-ui/public/js/logger.d.ts +6 -0
  68. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
  69. package/dist/homebridge-ui/public/js/logger.js +12 -0
  70. package/dist/homebridge-ui/public/js/logger.js.map +1 -0
  71. package/dist/homebridge-ui/public/js/logger.ts +11 -0
  72. package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
  73. package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
  74. package/dist/homebridge-ui/public/js/modal.js +26 -0
  75. package/dist/homebridge-ui/public/js/modal.js.map +1 -0
  76. package/dist/homebridge-ui/public/js/modal.ts +28 -0
  77. package/dist/homebridge-ui/public/js/modals.d.ts +2 -0
  78. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
  79. package/dist/homebridge-ui/public/js/modals.js +132 -0
  80. package/dist/homebridge-ui/public/js/modals.js.map +1 -0
  81. package/dist/homebridge-ui/public/js/modals.ts +153 -0
  82. package/dist/homebridge-ui/public/js/render.d.ts +6 -0
  83. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
  84. package/dist/homebridge-ui/public/js/render.js +166 -0
  85. package/dist/homebridge-ui/public/js/render.js.map +1 -0
  86. package/dist/homebridge-ui/public/js/render.ts +190 -0
  87. package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
  88. package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
  89. package/dist/homebridge-ui/public/js/toast.js +29 -0
  90. package/dist/homebridge-ui/public/js/toast.js.map +1 -0
  91. package/dist/homebridge-ui/public/js/toast.ts +37 -0
  92. package/dist/homebridge-ui/public/js/types.d.ts +23 -0
  93. package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
  94. package/dist/homebridge-ui/public/js/types.js +2 -0
  95. package/dist/homebridge-ui/public/js/types.js.map +1 -0
  96. package/dist/homebridge-ui/public/js/types.ts +26 -0
  97. package/dist/homebridge-ui/server.d.ts +1 -3
  98. package/dist/homebridge-ui/server.d.ts.map +1 -1
  99. package/dist/homebridge-ui/server.js +10 -561
  100. package/dist/homebridge-ui/server.js.map +1 -1
  101. package/dist/homebridge-ui/utils/config-parser.d.ts +35 -0
  102. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
  103. package/dist/homebridge-ui/utils/config-parser.js +87 -0
  104. package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
  105. package/dist/homebridge-ui/utils/logger.d.ts +7 -0
  106. package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
  107. package/dist/homebridge-ui/utils/logger.js +17 -0
  108. package/dist/homebridge-ui/utils/logger.js.map +1 -0
  109. package/dist/platform.d.ts.map +1 -1
  110. package/dist/platform.js +6 -2
  111. package/dist/platform.js.map +1 -1
  112. package/dist/switchbotClient.d.ts.map +1 -1
  113. package/dist/switchbotClient.js +2 -0
  114. package/dist/switchbotClient.js.map +1 -1
  115. package/docs/variables/default.html +1 -1
  116. package/eslint.config.js +5 -0
  117. package/nodemon.json +2 -2
  118. package/package.json +20 -10
  119. package/scripts/build-ui.js +36 -0
  120. package/scripts/free-dev-ports.mjs +105 -0
  121. package/scripts/sync-device-types.mjs +28 -0
  122. package/src/device-types.ts +145 -0
  123. package/src/deviceFactory.ts +71 -31
  124. package/src/devices/genericDevice.ts +5 -2
  125. package/src/homebridge-ui/endpoints/config.ts +79 -0
  126. package/src/homebridge-ui/endpoints/credentials.ts +74 -0
  127. package/src/homebridge-ui/endpoints/devices.ts +321 -0
  128. package/src/homebridge-ui/endpoints/discovery.ts +191 -0
  129. package/src/homebridge-ui/public/index.html +144 -624
  130. package/src/homebridge-ui/public/js/api.ts +189 -0
  131. package/src/homebridge-ui/public/js/app.ts +21 -0
  132. package/src/homebridge-ui/public/js/constants.ts +1 -0
  133. package/src/homebridge-ui/public/js/credentials.ts +100 -0
  134. package/src/homebridge-ui/public/js/devices-delete.ts +113 -0
  135. package/src/homebridge-ui/public/js/devices.ts +78 -0
  136. package/src/homebridge-ui/public/js/discovery.ts +123 -0
  137. package/src/homebridge-ui/public/js/logger.ts +11 -0
  138. package/src/homebridge-ui/public/js/modal.ts +28 -0
  139. package/src/homebridge-ui/public/js/modals.ts +153 -0
  140. package/src/homebridge-ui/public/js/render.ts +190 -0
  141. package/src/homebridge-ui/public/js/toast.ts +37 -0
  142. package/src/homebridge-ui/public/js/types.ts +26 -0
  143. package/src/homebridge-ui/server.ts +11 -647
  144. package/src/homebridge-ui/utils/config-parser.ts +108 -0
  145. package/src/homebridge-ui/utils/logger.ts +17 -0
  146. package/src/platform.ts +6 -2
  147. package/src/switchbotClient.ts +2 -0
  148. package/switchbot-homebridge-switchbot-5.0.0.tgz +0 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/homebridge-ui/public/js/types.ts", "../../../../src/homebridge-ui/public/js/logger.ts", "../../../../src/homebridge-ui/public/js/api.ts", "../../../../src/homebridge-ui/public/js/modal.ts", "../../../../src/homebridge-ui/public/js/toast.ts", "../../../../src/homebridge-ui/public/js/discovery.ts", "../../../../src/device-types.ts", "../../../../src/homebridge-ui/public/js/constants.ts", "../../../../src/homebridge-ui/public/js/modals.ts", "../../../../src/homebridge-ui/public/js/devices-delete.ts", "../../../../src/homebridge-ui/public/js/render.ts", "../../../../src/homebridge-ui/public/js/devices.ts", "../../../../src/homebridge-ui/public/js/credentials.ts", "../../../../src/homebridge-ui/public/js/app.ts"],
4
+ "sourcesContent": ["// Global homebridge API type declarations\ndeclare global {\n interface HomebridgeToastApi {\n success?: (message: string, title?: string) => void\n error?: (message: string, title?: string) => void\n warning?: (message: string, title?: string) => void\n info?: (message: string, title?: string) => void\n }\n\n interface HomebridgePluginUiAPI {\n request: (endpoint: string, params?: any) => Promise<any>\n getPluginConfig?: () => Promise<any[]>\n updatePluginConfig?: (config: any[]) => Promise<void>\n savePluginConfig?: () => Promise<void>\n closeSettings?: () => void\n showSpinner?: () => void\n hideSpinner?: () => void\n disableSaveButton?: () => void\n enableSaveButton?: () => void\n toast?: HomebridgeToastApi\n }\n\n let homebridge: HomebridgePluginUiAPI\n}\n\nexport {}\n", "export const uiLog = {\n info: (message: string, ...parameters: any[]): void => {\n console.warn('[SwitchBot UI]', message, ...parameters)\n },\n warn: (message: string, ...parameters: any[]): void => {\n console.warn('[SwitchBot UI]', message, ...parameters)\n },\n error: (message: string, ...parameters: any[]): void => {\n console.error('[SwitchBot UI]', message, ...parameters)\n },\n}\n", "// API wrapper functions for communicating with the Homebridge UI server\nimport './types.js'\nimport { uiLog } from './logger.js'\n\nfunction isSwitchBotPlatformConfig(block: any): boolean {\n const platformName = String(block?.platform || block?.name || '').toLowerCase()\n return (\n platformName === 'switchbot'\n || platformName === '@switchbot/homebridge-switchbot'\n || platformName.includes('switchbot')\n )\n}\n\nexport async function syncParentPluginConfigFromDisk(autoSave = false): Promise<boolean> {\n try {\n if (\n typeof homebridge.getPluginConfig !== 'function'\n || typeof homebridge.updatePluginConfig !== 'function'\n ) {\n uiLog.warn('Parent config sync API not available')\n return false\n }\n\n const diskResp = await homebridge.request('/platform-config', {})\n if (!diskResp || diskResp.success === false || !diskResp.data) {\n uiLog.warn('Failed to fetch fresh platform config from disk')\n return false\n }\n\n const pluginConfigBlocks = await homebridge.getPluginConfig()\n if (!Array.isArray(pluginConfigBlocks) || !pluginConfigBlocks.length) {\n uiLog.warn('No plugin config blocks returned from Homebridge')\n return false\n }\n\n const index = pluginConfigBlocks.findIndex(block => isSwitchBotPlatformConfig(block))\n if (index < 0) {\n uiLog.warn('SwitchBot platform block not found in Homebridge plugin config')\n return false\n }\n\n pluginConfigBlocks[index] = diskResp.data\n await homebridge.updatePluginConfig(pluginConfigBlocks)\n\n // Auto-save to disk if requested - prevents parent UI from overwriting with stale cache\n if (autoSave && typeof homebridge.savePluginConfig === 'function') {\n uiLog.info('Auto-saving config to disk...')\n await homebridge.savePluginConfig()\n uiLog.info('Config saved successfully')\n }\n\n return true\n } catch (e) {\n uiLog.warn('Failed to sync parent plugin config cache:', e)\n return false\n }\n}\n\nexport async function fetchCredentialStatus(): Promise<any> {\n try {\n const resp = await homebridge.request('/credentials', {})\n uiLog.info('Load credentials response:', resp)\n\n if (!resp || resp.success === false) {\n uiLog.error('Failed to load credentials:', resp)\n return null\n }\n\n return resp.data || {}\n } catch (e) {\n uiLog.error('Error loading credentials:', e)\n return null\n }\n}\n\nexport async function saveCredentials(token: string, secret: string): Promise<any> {\n try {\n uiLog.info('Saving credentials...')\n const resp = await homebridge.request('/credentials', { token, secret })\n uiLog.info('Save response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.message || 'Save failed')\n }\n\n return resp.data || resp\n } catch (e) {\n throw e\n }\n}\n\nexport async function fetchDevices(): Promise<any[]> {\n try {\n const resp = await homebridge.request('/devices', {})\n if (!resp || resp.success === false) { throw new Error(resp?.data?.message || 'request failed') }\n return resp.data || []\n } catch (e) {\n uiLog.error('Error fetching devices:', e)\n return []\n }\n}\n\nexport async function discoverDevices(): Promise<any[]> {\n try {\n const resp = await homebridge.request('/discover', {})\n uiLog.info('Discover response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Discovery failed')\n }\n\n return resp.data || []\n } catch (e) {\n throw e\n }\n}\n\nexport async function addDevice(deviceId: string, name: string, type: string): Promise<any> {\n try {\n uiLog.info('Adding device to config:', { deviceId, name, type })\n const resp = await homebridge.request('/add-device', { deviceId, name, type })\n uiLog.info('Add device response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Failed to add device')\n }\n\n return resp.data || resp\n } catch (e) {\n throw e\n }\n}\n\nexport async function addDevicesInBulk(\n devices: Array<{ deviceId: string, name: string, type: string }>,\n): Promise<any> {\n try {\n const resp = await homebridge.request('/add-devices', { devices })\n uiLog.info('Bulk add response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Bulk add failed')\n }\n\n return resp.data || resp\n } catch (e) {\n throw e\n }\n}\n\nexport async function updateDevice(\n deviceId: string,\n configDeviceName?: string,\n configDeviceType?: string,\n): Promise<any> {\n try {\n const params: any = { deviceId }\n if (configDeviceName) { params.configDeviceName = configDeviceName }\n if (configDeviceType) { params.configDeviceType = configDeviceType }\n\n uiLog.info('[Update Device] Sending update request with params:', params)\n const resp = await homebridge.request('/update-device', params)\n uiLog.info('[Update Device] Update response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Failed to update device')\n }\n\n return resp.data || resp\n } catch (e) {\n throw e\n }\n}\n\nexport async function deleteDevice(deviceId: string): Promise<any> {\n try {\n uiLog.info('Sending delete request for deviceId:', deviceId)\n const resp = await homebridge.request('/delete-device', { deviceId })\n uiLog.info('Delete response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Failed to delete device')\n }\n\n return resp.data || resp\n } catch (e) {\n throw e\n }\n}\n", "import './types.js'\nimport { uiLog } from './logger.js'\n\nfunction callUiMethod(name: keyof HomebridgePluginUiAPI): void {\n try {\n const value = homebridge?.[name]\n if (typeof value === 'function') {\n const fn = value as () => void\n fn()\n }\n } catch (e) {\n uiLog.warn(`Homebridge UI method ${String(name)} failed:`, e)\n }\n}\n\nexport function showBusyUi(): void {\n callUiMethod('disableSaveButton')\n callUiMethod('showSpinner')\n}\n\nexport function hideBusyUi(): void {\n callUiMethod('hideSpinner')\n callUiMethod('enableSaveButton')\n}\n\nexport function closeSettingsModal(): void {\n callUiMethod('closeSettings')\n}\n", "import './types.js'\nimport { uiLog } from './logger.js'\n\nfunction showToast(\n method: 'success' | 'error' | 'warning' | 'info',\n message: string,\n title = 'SwitchBot',\n): void {\n try {\n const toast = homebridge?.toast\n const fn = toast?.[method]\n if (typeof fn === 'function') {\n fn(message, title)\n return\n }\n\n uiLog.info(`[Toast:${method}] ${title} - ${message}`)\n } catch (e) {\n uiLog.warn(`Toast ${method} failed:`, e)\n }\n}\n\nexport function toastSuccess(message: string, title?: string): void {\n showToast('success', message, title)\n}\n\nexport function toastError(message: string, title?: string): void {\n showToast('error', message, title)\n}\n\nexport function toastWarning(message: string, title?: string): void {\n showToast('warning', message, title)\n}\n\nexport function toastInfo(message: string, title?: string): void {\n showToast('info', message, title)\n}\n", "import {\n addDevicesInBulk,\n discoverDevices as apiDiscoverDevices,\n fetchDevices,\n syncParentPluginConfigFromDisk,\n} from './api.js'\nimport { loadConfiguredDevices } from './devices.js'\nimport { uiLog } from './logger.js'\nimport { hideBusyUi, showBusyUi } from './modal.js'\nimport { renderDiscoveredDevices } from './render.js'\nimport { toastError, toastInfo, toastSuccess, toastWarning } from './toast.js'\n\nexport async function discoverDevices(): Promise<void> {\n const btn = document.getElementById('discoverBtn') as HTMLButtonElement\n const status = document.getElementById('discoverStatus')\n const list = document.getElementById('discoveredList')\n const autoAddAll = (document.getElementById('autoAddAllCheckbox') as HTMLInputElement)?.checked\n\n if (!btn || !status || !list) { return }\n\n try {\n showBusyUi()\n btn.disabled = true\n btn.textContent = '\uD83D\uDD0D Discovering...'\n status.textContent = 'Searching SwitchBot account...'\n status.classList.remove('error')\n\n const devicesRaw = await apiDiscoverDevices()\n uiLog.info('Discover response:', devicesRaw)\n\n // Deduplicate by device ID\n const devices = devicesRaw.filter((d, index, arr) => !!d?.id && arr.findIndex(x => x?.id === d.id) === index)\n\n if (!devices.length) {\n status.textContent = 'No devices found in your SwitchBot account'\n toastInfo('No devices found in your SwitchBot account')\n list.style.display = 'none'\n return\n }\n\n // If auto-add is enabled, add all devices immediately using bulk endpoint\n if (autoAddAll) {\n status.textContent = `Auto-adding ${devices.length} device(s)...`\n\n try {\n const bulkResult = await addDevicesInBulk(\n devices.map(d => ({\n deviceId: d.id,\n name: d.name,\n type: d.type,\n })),\n )\n uiLog.info('Bulk add response:', bulkResult)\n\n if (!bulkResult || bulkResult.success === false) {\n throw new Error(bulkResult?.data?.message || 'Bulk add failed')\n }\n\n const addedCount\n = bulkResult?.addedCount\n ?? bulkResult?.data?.addedCount\n ?? 0\n const skippedCount\n = bulkResult?.skippedCount\n ?? bulkResult?.data?.skippedCount\n ?? 0\n status.textContent = `\u2713 Added ${addedCount} device(s)${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`\n if (addedCount > 0) {\n toastSuccess(`Added ${addedCount} device(s)${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`)\n } else if (skippedCount > 0) {\n toastWarning(`No new devices were added (${skippedCount} skipped)`)\n }\n status.classList.remove('error')\n list.style.display = 'none'\n\n // Sync parent Homebridge form cache and auto-save to prevent cache overwrite\n // Run sync when discovery returned devices (even if backend response shape changes)\n if (devices.length > 0) {\n const synced = await syncParentPluginConfigFromDisk(true)\n status.textContent += synced\n ? ' - Config saved automatically.'\n : ' - Warning: config may not persist until you close/reopen settings.'\n\n if (synced) {\n toastSuccess('Configuration synced and saved automatically')\n } else {\n toastWarning('Configuration sync failed; close and reopen settings before Save')\n }\n }\n } catch (e) {\n uiLog.error('Bulk add error:', e)\n status.textContent = `\u2717 Error: ${e instanceof Error ? e.message : 'Failed to add devices'}`\n status.classList.add('error')\n toastError(e instanceof Error ? e.message : 'Failed to add devices')\n }\n\n // Refresh the configured devices list\n await loadConfiguredDevices()\n return\n }\n\n status.textContent = `Found ${devices.length} device(s)`\n const ul = await renderDiscoveredDevices(devices)\n document.getElementById('discoveredDevices')?.replaceWith(ul)\n ul.id = 'discoveredDevices'\n list.style.display = 'block'\n } catch (e) {\n uiLog.error('Discovery error:', e)\n status.textContent = `Error: ${e instanceof Error ? e.message : 'Discovery failed'}`\n status.classList.add('error')\n toastError(e instanceof Error ? e.message : 'Discovery failed')\n list.style.display = 'none'\n } finally {\n hideBusyUi()\n btn.disabled = false\n btn.textContent = '\uD83D\uDD0D Discover Devices'\n }\n}\n\nexport async function addDeviceToConfig(device: any): Promise<void> {\n const { addDeviceToConfig: addDevice } = await import('./devices.js')\n await addDevice(device)\n}\n", "export const DEVICE_TYPES = {\n 'Window Coverings': ['Blind Tilt', 'Curtain', 'Curtain3', 'Roller Shade'],\n 'Locks & Access': [\n 'Keypad',\n 'Keypad Touch',\n 'Keypad Vision',\n 'Keypad Vision Pro',\n 'Lock Lite',\n 'Smart Lock',\n 'Smart Lock Pro',\n 'Smart Lock Ultra',\n 'Video Doorbell',\n ],\n 'Sensors': ['Contact Sensor', 'Motion Sensor', 'Presence Sensor', 'Water Detector'],\n 'Lighting': [\n 'Candle Warmer Lamp',\n 'Ceiling Light',\n 'Ceiling Light Pro',\n 'Color Bulb',\n 'Floor Lamp',\n 'RGBIC Neon Rope Light',\n 'RGBIC Neon Wire Rope Light',\n 'RGBICWW Floor Lamp',\n 'RGBICWW Strip Light',\n 'Strip Light',\n 'Strip Light 3',\n ],\n 'Climate Control': [\n 'Air Purifier PM2.5',\n 'Air Purifier Table PM2.5',\n 'Air Purifier VOC',\n 'Air Purifier Table VOC',\n 'Battery Circulator Fan',\n 'Circulator Fan',\n 'Humidifier',\n 'Humidifier2',\n 'Meter',\n 'MeterPlus',\n 'MeterPro',\n 'MeterPro(CO2)',\n 'Smart Radiator Thermostat',\n 'Standing Circulator Fan',\n 'WoIOSensor',\n ],\n 'Plugs & Switches': [\n 'Garage Door Opener',\n 'Plug',\n 'Plug Mini (EU)',\n 'Plug Mini (JP)',\n 'Plug Mini (US)',\n 'Relay Switch 1',\n 'Relay Switch 1PM',\n 'Relay Switch 2PM',\n ],\n 'Robot Vacuums': [\n 'K10+',\n 'K10+ Pro',\n 'Robot Vacuum Cleaner K10+ Pro Combo',\n 'Robot Vacuum Cleaner K11+',\n 'Robot Vacuum Cleaner K20 Plus Pro',\n 'Robot Vacuum Cleaner S1',\n 'Robot Vacuum Cleaner S1 Plus',\n 'Robot Vacuum Cleaner S10',\n 'Robot Vacuum Cleaner S20',\n ],\n 'Hubs': ['AI Hub', 'Hub', 'Hub 2', 'Hub 3', 'Hub Mini', 'Hub Plus'],\n 'Cameras': [\n 'Indoor Cam',\n 'Pan/Tilt Cam',\n 'Pan/Tilt Cam 2K',\n 'Pan/Tilt Cam Plus 2K',\n 'Pan/Tilt Cam Plus 3K',\n ],\n 'IR Devices': [\n 'Air Conditioner',\n 'Air Purifier',\n 'Camera',\n 'DVD',\n 'Fan',\n 'Light',\n 'Others',\n 'Projector',\n 'Set Top Box',\n 'Speaker',\n 'Streamer',\n 'TV',\n 'Vacuum Cleaner',\n 'Water Heater',\n ],\n 'Other Devices': ['AI Art Frame', 'Bot', 'Home Climate Panel', 'Remote', 'remote with screen'],\n} as const\n\nexport const DEVICE_TYPE_NORMALIZATION_MAP: Record<string, string> = {\n 'motion sensor': 'motion',\n 'contact sensor': 'contact',\n 'presence sensor': 'contact',\n 'water detector': 'water detector',\n 'humidifier2': 'humidifier',\n 'circulator fan': 'fan',\n 'battery circulator fan': 'fan',\n 'standing circulator fan': 'fan',\n 'smart lock': 'lock',\n 'smart lock pro': 'lock',\n 'smart lock ultra': 'lock',\n 'lock lite': 'lock',\n 'keypad': 'lock',\n 'keypad touch': 'lock',\n 'keypad vision': 'lock',\n 'keypad vision pro': 'lock',\n 'color bulb': 'light',\n 'ceiling light': 'light',\n 'ceiling light pro': 'light',\n 'candle warmer lamp': 'light',\n 'floor lamp': 'light',\n 'rgbic neon rope light': 'lightstrip',\n 'rgbic neon wire rope light': 'lightstrip',\n 'rgbicww floor lamp': 'lightstrip',\n 'rgbicww strip light': 'lightstrip',\n 'strip light': 'strip light',\n 'strip light 3': 'strip light',\n 'robot vacuum cleaner s1': 'vacuum',\n 'robot vacuum cleaner s1 plus': 'vacuum',\n 'robot vacuum cleaner s10': 'vacuum',\n 'robot vacuum cleaner s20': 'vacuum',\n 'robot vacuum cleaner k10+ pro combo': 'wosweeper',\n 'robot vacuum cleaner k11+': 'wosweeper',\n 'robot vacuum cleaner k20 plus pro': 'wosweeper',\n 'plug mini (eu)': 'plug mini (eu)',\n 'plug mini (jp)': 'plug mini (jp)',\n 'plug mini (us)': 'plug mini (us)',\n 'relay switch 1': 'relay switch 1',\n 'relay switch 1pm': 'relay switch 1pm',\n 'relay switch 2pm': 'relay switch 2pm',\n 'blind tilt': 'blind tilt',\n 'roller shade': 'roller shade',\n 'curtain3': 'curtain3',\n 'hub 2': 'hub 2',\n 'meterplus': 'meterplus',\n 'meterpro': 'meterpro',\n 'meterpro(co2)': 'meterpro(co2)',\n 'walletfinder': 'walletfinder',\n 'k10+': 'k10+',\n 'k10+ pro': 'k10+ pro',\n 'k10+ pro (wosweeperminipro)': 'k10+ pro (wosweeperminipro)',\n}\n", "export { DEVICE_TYPES } from '../../../device-types.js'\n", "import { DEVICE_TYPES } from './constants.js'\nimport { uiLog } from './logger.js'\n\nexport async function editDevice(device: any): Promise<void> {\n // Create modal dialog for editing\n const div = document.createElement('div')\n div.style.position = 'fixed'\n div.style.top = '0'\n div.style.left = '0'\n div.style.width = '100%'\n div.style.height = '100%'\n div.style.background = 'rgba(0,0,0,0.7)'\n div.style.display = 'flex'\n div.style.alignItems = 'center'\n div.style.justifyContent = 'center'\n div.style.zIndex = '9999'\n\n const modal = document.createElement('div')\n modal.style.background = getComputedStyle(document.body).backgroundColor\n modal.style.color = getComputedStyle(document.body).color\n modal.style.padding = '24px'\n modal.style.borderRadius = '8px'\n modal.style.minWidth = '400px'\n modal.style.boxShadow = '0 10px 40px rgba(0,0,0,0.3)'\n\n const title = document.createElement('h3')\n title.textContent = 'Edit Device'\n title.style.marginTop = '0'\n\n const nameLabel = document.createElement('label')\n nameLabel.textContent = 'Device Name'\n nameLabel.style.display = 'block'\n nameLabel.style.marginBottom = '8px'\n nameLabel.style.fontWeight = '500'\n\n const nameInput = document.createElement('input')\n nameInput.type = 'text'\n nameInput.value = device.name || device.id\n nameInput.style.width = '100%'\n nameInput.style.marginBottom = '16px'\n\n // Read-only API device type field\n const apiTypeLabel = document.createElement('label')\n apiTypeLabel.textContent = 'Device Type (API - Read Only)'\n apiTypeLabel.style.display = 'block'\n apiTypeLabel.style.marginBottom = '8px'\n apiTypeLabel.style.fontWeight = '500'\n\n const apiTypeInput = document.createElement('input')\n apiTypeInput.type = 'text'\n apiTypeInput.value = device.deviceType || device.type || 'Unknown'\n apiTypeInput.readOnly = true\n apiTypeInput.style.width = '100%'\n apiTypeInput.style.marginBottom = '16px'\n apiTypeInput.style.opacity = '0.6'\n apiTypeInput.style.cursor = 'not-allowed'\n\n // Editable config device type dropdown\n const typeLabel = document.createElement('label')\n typeLabel.textContent = 'Config Device Type (Override)'\n typeLabel.style.display = 'block'\n typeLabel.style.marginBottom = '8px'\n typeLabel.style.fontWeight = '500'\n\n const typeSelect = document.createElement('select')\n typeSelect.style.width = '100%'\n typeSelect.style.padding = '8px'\n typeSelect.style.marginBottom = '16px'\n typeSelect.style.borderRadius = '4px'\n typeSelect.style.background = getComputedStyle(nameInput).background\n typeSelect.style.color = getComputedStyle(nameInput).color\n typeSelect.style.border = getComputedStyle(nameInput).border\n\n // Add option groups with all API device types\n Object.keys(DEVICE_TYPES).forEach((categoryName) => {\n const optgroup = document.createElement('optgroup')\n optgroup.label = categoryName\n\n DEVICE_TYPES[categoryName as keyof typeof DEVICE_TYPES].forEach((deviceType) => {\n const opt = document.createElement('option')\n opt.value = deviceType\n opt.text = deviceType\n\n // Select current configDeviceType if set, otherwise match API deviceType\n const currentType = device.configDeviceType || device.deviceType || device.type || ''\n opt.selected = currentType === deviceType\n optgroup.appendChild(opt)\n })\n\n typeSelect.appendChild(optgroup)\n })\n\n const buttons = document.createElement('div')\n buttons.style.display = 'flex'\n buttons.style.gap = '8px'\n buttons.style.justifyContent = 'flex-end'\n\n const errorMessage = document.createElement('div')\n errorMessage.style.color = '#ef4444'\n errorMessage.style.marginBottom = '12px'\n errorMessage.style.fontSize = '12px'\n errorMessage.style.display = 'none'\n\n const cancelBtn = document.createElement('button')\n cancelBtn.textContent = 'Cancel'\n cancelBtn.style.background = '#666'\n cancelBtn.onclick = () => div.remove()\n\n const saveBtn = document.createElement('button')\n saveBtn.textContent = 'Save'\n saveBtn.onclick = async () => {\n try {\n const { updateDevice, syncParentPluginConfigFromDisk, fetchDevices } = await import('./api.js')\n const { renderDeviceList } = await import('./render.js')\n\n const params = {\n deviceId: device.id,\n configDeviceName: nameInput.value || undefined,\n configDeviceType: typeSelect.value,\n }\n\n await updateDevice(params.deviceId, params.configDeviceName, params.configDeviceType)\n await syncParentPluginConfigFromDisk()\n\n // Refresh device list\n uiLog.info('[Edit Device] Refreshing device list after update')\n const list = await fetchDevices()\n renderDeviceList(list)\n div.remove()\n } catch (e) {\n uiLog.error('Update error:', e)\n errorMessage.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to update device'}`\n errorMessage.style.display = 'block'\n }\n }\n\n buttons.appendChild(cancelBtn)\n buttons.appendChild(saveBtn)\n\n modal.appendChild(title)\n modal.appendChild(nameLabel)\n modal.appendChild(nameInput)\n modal.appendChild(apiTypeLabel)\n modal.appendChild(apiTypeInput)\n modal.appendChild(typeLabel)\n modal.appendChild(typeSelect)\n modal.appendChild(errorMessage)\n modal.appendChild(buttons)\n\n div.appendChild(modal)\n document.body.appendChild(div)\n nameInput.focus()\n}\n", "import { deleteDevice as apiDeleteDevice, syncParentPluginConfigFromDisk, fetchDevices } from './api.js'\nimport { uiLog } from './logger.js'\nimport { hideBusyUi, showBusyUi } from './modal.js'\nimport { renderDeviceList } from './render.js'\nimport { toastError, toastSuccess, toastWarning } from './toast.js'\n\nasync function confirmDeleteDialog(deviceNameOrId: string): Promise<boolean> {\n return new Promise((resolve) => {\n const overlay = document.createElement('div')\n overlay.style.position = 'fixed'\n overlay.style.top = '0'\n overlay.style.left = '0'\n overlay.style.width = '100%'\n overlay.style.height = '100%'\n overlay.style.background = 'rgba(0,0,0,0.6)'\n overlay.style.display = 'flex'\n overlay.style.alignItems = 'center'\n overlay.style.justifyContent = 'center'\n overlay.style.zIndex = '10000'\n\n const modal = document.createElement('div')\n modal.style.background = getComputedStyle(document.body).backgroundColor\n modal.style.color = getComputedStyle(document.body).color\n modal.style.padding = '20px'\n modal.style.borderRadius = '10px'\n modal.style.minWidth = '340px'\n modal.style.maxWidth = '90vw'\n modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)'\n\n const title = document.createElement('h3')\n title.textContent = 'Delete Device'\n title.style.margin = '0 0 10px 0'\n\n const message = document.createElement('p')\n message.textContent = `Remove \"${deviceNameOrId}\" from configuration?`\n message.style.margin = '0 0 16px 0'\n\n const actions = document.createElement('div')\n actions.style.display = 'flex'\n actions.style.justifyContent = 'flex-end'\n actions.style.gap = '8px'\n\n const cancelBtn = document.createElement('button')\n cancelBtn.textContent = 'Cancel'\n cancelBtn.style.background = '#6b7280'\n\n const deleteBtn = document.createElement('button')\n deleteBtn.textContent = 'Delete'\n deleteBtn.style.background = '#ef4444'\n\n const cleanup = (result: boolean) => {\n overlay.remove()\n resolve(result)\n }\n\n cancelBtn.onclick = () => cleanup(false)\n deleteBtn.onclick = () => cleanup(true)\n overlay.addEventListener('click', (event) => {\n if (event.target === overlay) {\n cleanup(false)\n }\n })\n\n actions.appendChild(cancelBtn)\n actions.appendChild(deleteBtn)\n modal.appendChild(title)\n modal.appendChild(message)\n modal.appendChild(actions)\n overlay.appendChild(modal)\n document.body.appendChild(overlay)\n })\n}\n\nexport async function deleteDeviceFromConfig(deviceId: string, deviceName: string): Promise<void> {\n uiLog.info('Delete button clicked for device:', deviceId, deviceName)\n\n const confirmed = await confirmDeleteDialog(deviceName || deviceId)\n if (!confirmed) {\n uiLog.info('Delete cancelled by user')\n return\n }\n\n try {\n showBusyUi()\n uiLog.info('Sending delete request for deviceId:', deviceId)\n const resp = await apiDeleteDevice(deviceId)\n uiLog.info('Delete response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Failed to delete device')\n }\n\n uiLog.info('Syncing parent config from disk...')\n const synced = await syncParentPluginConfigFromDisk(true)\n if (!synced) {\n toastWarning('Device deleted, but configuration sync failed')\n }\n\n uiLog.info('Refreshing device list...')\n // Refresh device list\n const list = await fetchDevices()\n uiLog.info('Rendering devices:', list.length)\n renderDeviceList(list)\n\n uiLog.info('\u2713 Device deleted successfully')\n toastSuccess(`Device \"${deviceName || deviceId}\" deleted successfully`)\n } catch (e) {\n uiLog.error('Delete error:', e)\n toastError(e instanceof Error ? e.message : 'Failed to delete device')\n } finally {\n hideBusyUi()\n }\n}\n", "import type { DEVICE_TYPES } from './constants.js'\n\nexport function renderBadge(text: string, style: string): HTMLElement {\n const badge = document.createElement('span')\n badge.textContent = text\n badge.style.cssText = style\n return badge\n}\n\nexport function renderConnectionBadge(connectionType: string): HTMLElement | null {\n if (!connectionType) { return null }\n\n const badge = renderBadge(connectionType, '')\n\n if (connectionType === 'BLE') {\n badge.style.cssText\n = 'background: #4285f4; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;'\n } else if (connectionType === 'Both') {\n badge.style.cssText\n = 'background: #34a853; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;'\n } else {\n badge.style.cssText\n = 'background: #9e9e9e; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;'\n }\n\n return badge\n}\n\nexport function renderIRBadge(): HTMLElement {\n return renderBadge(\n 'IR',\n 'background: #ff6b35; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;',\n )\n}\n\nexport async function renderDiscoveredDevices(devices: any[]): Promise<HTMLElement> {\n const ul = document.createElement('ul')\n ul.style.maxHeight = '300px'\n ul.style.overflowY = 'auto'\n\n const { addDeviceToConfig } = await import('./discovery.js')\n\n for (const d of devices) {\n const li = document.createElement('li')\n li.className = 'device-item'\n\n const info = document.createElement('div')\n info.style.flex = '1'\n\n const name = document.createElement('div')\n name.style.fontWeight = '500'\n name.textContent = d.name || d.id\n\n // Add connection type badge\n if (d.connectionType) {\n name.appendChild(document.createTextNode(' '))\n const badge = renderConnectionBadge(d.connectionType)\n if (badge) { name.appendChild(badge) }\n }\n\n // Add IR badge if it's an IR device\n if (d.isIR) {\n name.appendChild(document.createTextNode(' '))\n name.appendChild(renderIRBadge())\n }\n\n const details = document.createElement('div')\n details.style.fontSize = '12px'\n details.style.opacity = '0.75'\n\n let detailsText = `ID: ${d.id} | Type: ${d.type} | Model: ${d.model || 'N/A'}`\n if (d.hubDeviceId) { detailsText += ` | Hub: ${d.hubDeviceId}` }\n if (d.rssi) { detailsText += ` | Signal: ${d.rssi} dBm` }\n if (d.address) { detailsText += ` | MAC: ${d.address}` }\n details.textContent = detailsText\n\n info.appendChild(name)\n info.appendChild(details)\n\n const addBtn = document.createElement('button')\n addBtn.textContent = '+ Add'\n addBtn.style.marginLeft = '8px'\n addBtn.onclick = async () => {\n await addDeviceToConfig(d)\n }\n\n li.appendChild(info)\n li.appendChild(addBtn)\n ul.appendChild(li)\n }\n\n return ul\n}\n\nexport function renderDeviceList(list: any[]): void {\n const ul = document.getElementById('devices')\n const status = document.getElementById('status')\n\n if (!ul || !status) { return }\n\n if (!list.length) {\n status.textContent = 'No devices found in config.'\n ul.innerHTML = ''\n return\n }\n\n status.textContent = `Found ${list.length} device(s)`\n ul.innerHTML = ''\n\n for (const d of list) {\n const li = document.createElement('li')\n li.className = 'device-item'\n\n const info = document.createElement('div')\n info.style.flex = '1'\n\n const name = document.createElement('div')\n name.style.fontWeight = '500'\n name.textContent = d.name || d.id\n\n const code = document.createElement('code')\n code.textContent = d.id\n code.style.fontSize = '12px'\n code.style.opacity = '0.75'\n code.style.marginLeft = '8px'\n\n const meta = document.createElement('div')\n meta.style.opacity = '0.75'\n meta.style.marginTop = '4px'\n meta.style.fontSize = '12px'\n\n const typeText = d.type ? `type: ${d.type}` : ''\n const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : ''\n const roomText = d.room ? `room: ${d.room}` : ''\n meta.textContent = [typeText, connText, roomText].filter(Boolean).join(' | ')\n\n info.appendChild(name)\n info.appendChild(code)\n info.appendChild(meta)\n\n const buttons = document.createElement('div')\n buttons.style.display = 'flex'\n buttons.style.gap = '8px'\n\n const editBtn = document.createElement('button')\n editBtn.textContent = '\u270F\uFE0F Edit'\n editBtn.style.padding = '6px 12px'\n editBtn.style.fontSize = '12px'\n editBtn.onclick = async () => {\n const { editDevice } = await import('./modals.js')\n await editDevice(d)\n }\n\n const copyBtn = document.createElement('button')\n copyBtn.textContent = 'Copy ID'\n copyBtn.style.padding = '6px 12px'\n copyBtn.style.fontSize = '12px'\n copyBtn.addEventListener('click', async () => {\n try {\n await navigator.clipboard.writeText(d.id)\n copyBtn.textContent = 'Copied'\n copyBtn.classList.add('success')\n setTimeout(() => {\n copyBtn.textContent = 'Copy ID'\n copyBtn.classList.remove('success')\n }, 1200)\n } catch (e) {\n alert('Failed to copy')\n }\n })\n\n const deleteBtn = document.createElement('button')\n deleteBtn.textContent = '\uD83D\uDDD1\uFE0F Delete'\n deleteBtn.style.padding = '6px 12px'\n deleteBtn.style.fontSize = '12px'\n deleteBtn.style.background = '#ef4444'\n deleteBtn.onclick = async () => {\n const { deleteDeviceFromConfig } = await import('./devices-delete.js')\n await deleteDeviceFromConfig(d.id, d.name || d.id)\n }\n\n buttons.appendChild(editBtn)\n buttons.appendChild(copyBtn)\n buttons.appendChild(deleteBtn)\n\n li.appendChild(info)\n li.appendChild(buttons)\n ul.appendChild(li)\n }\n}\n", "import { addDevice, fetchDevices, syncParentPluginConfigFromDisk } from './api.js'\nimport { uiLog } from './logger.js'\nimport { hideBusyUi, showBusyUi } from './modal.js'\nimport { renderDeviceList } from './render.js'\nimport { toastError, toastInfo, toastSuccess, toastWarning } from './toast.js'\n\nexport async function addDeviceToConfig(device: any, options: { refresh?: boolean, showStatus?: boolean } = {}): Promise<{ added: boolean }> {\n const { refresh = true, showStatus = true } = options\n\n try {\n showBusyUi()\n uiLog.info('Adding device to config:', device)\n const resp = await addDevice(device.id, device.name, device.type)\n uiLog.info('Add device response:', resp)\n\n if (!resp || resp.success === false) {\n throw new Error(resp?.data?.message || 'Failed to add device')\n }\n\n const alreadyExists = !!resp?.alreadyExists\n const message\n = resp?.message\n || (alreadyExists\n ? `Device \"${device.name}\" already in config`\n : `Device \"${device.name}\" added successfully!`)\n\n if (alreadyExists) {\n toastInfo(message)\n } else {\n toastSuccess(message)\n }\n\n if (showStatus) {\n const status = document.getElementById('discoverStatus')\n if (status) {\n status.textContent = (alreadyExists ? '\u2022 ' : '\u2713 ') + message\n status.classList.remove('error')\n status.classList.add('success-msg')\n\n // Sync parent Homebridge form cache and auto-save to prevent cache overwrite\n if (!alreadyExists) {\n const synced = await syncParentPluginConfigFromDisk(true)\n status.textContent += synced\n ? ' - Config saved automatically.'\n : ' - Warning: config may not persist until you close/reopen settings.'\n\n if (synced) {\n toastSuccess('Configuration synced and saved automatically')\n } else {\n toastWarning('Configuration sync failed; close and reopen settings before Save')\n }\n }\n }\n }\n\n if (refresh) {\n await loadConfiguredDevices()\n }\n\n return { added: !alreadyExists }\n } catch (e) {\n uiLog.error('Add device error:', e)\n const status = document.getElementById('discoverStatus')\n if (status) {\n status.textContent = `\u2717 Error: ${e instanceof Error ? e.message : 'Failed to add device'}`\n status.classList.add('error')\n }\n toastError(e instanceof Error ? e.message : 'Failed to add device')\n return { added: false }\n } finally {\n hideBusyUi()\n }\n}\n\nexport async function loadConfiguredDevices(): Promise<void> {\n const list = await fetchDevices()\n renderDeviceList(list)\n}\n", "import { fetchCredentialStatus as apiFetchCredentialStatus, saveCredentials as apiSaveCredentials, syncParentPluginConfigFromDisk } from './api.js'\nimport { uiLog } from './logger.js'\nimport { hideBusyUi, showBusyUi } from './modal.js'\nimport { toastError, toastSuccess, toastWarning } from './toast.js'\n\nexport async function loadCredentialStatus(): Promise<void> {\n try {\n const creds = await apiFetchCredentialStatus()\n\n if (!creds) {\n uiLog.error('Failed to load credentials')\n return\n }\n\n const tokenStatus = document.getElementById('tokenStatus')\n const secretStatus = document.getElementById('secretStatus')\n\n if (!tokenStatus || !secretStatus) { return }\n\n if (creds.hasToken) {\n tokenStatus.textContent = `\u2713 Configured (${creds.tokenLength} characters)`\n tokenStatus.classList.add('ok')\n } else {\n tokenStatus.textContent = 'Not configured'\n tokenStatus.classList.remove('ok')\n }\n\n if (creds.hasSecret) {\n secretStatus.textContent = `\u2713 Configured (${creds.secretLength} characters)`\n secretStatus.classList.add('ok')\n } else {\n secretStatus.textContent = 'Not configured'\n secretStatus.classList.remove('ok')\n }\n } catch (e) {\n uiLog.error('Error loading credentials:', e)\n }\n}\n\nexport async function saveCredentials(): Promise<void> {\n const token = (document.getElementById('token') as HTMLInputElement)?.value\n const secret = (document.getElementById('secret') as HTMLInputElement)?.value\n const saveStatus = document.getElementById('saveStatus')\n const saveBtn = document.getElementById('saveBtn') as HTMLButtonElement\n\n if (!saveStatus || !saveBtn) { return }\n\n if (!token || !secret) {\n saveStatus.textContent = 'Please enter both token and secret'\n saveStatus.classList.add('error')\n toastWarning('Please enter both token and secret')\n return\n }\n\n try {\n showBusyUi()\n saveBtn.disabled = true\n saveBtn.textContent = 'Saving...'\n uiLog.info('Saving credentials...')\n\n const result = await apiSaveCredentials(token, secret)\n uiLog.info('Save response:', result)\n\n const message = result?.message || 'Credentials saved successfully'\n saveStatus.textContent = `\u2713 ${message}`\n saveStatus.classList.remove('error')\n saveStatus.classList.add('success-msg')\n toastSuccess(message)\n\n const synced = await syncParentPluginConfigFromDisk(true)\n if (synced) {\n saveStatus.textContent += ' - Config saved automatically.'\n toastSuccess('Configuration synced and saved automatically')\n } else {\n toastWarning('Credentials saved, but configuration sync failed')\n }\n\n // Clear inputs after successful save\n ;(document.getElementById('token') as HTMLInputElement).value = ''\n ;(document.getElementById('secret') as HTMLInputElement).value = ''\n\n // Reload status to verify save\n setTimeout(() => loadCredentialStatus(), 500)\n\n // Clear status message\n setTimeout(() => {\n saveStatus.textContent = ''\n saveStatus.classList.remove('success-msg')\n }, 3000)\n } catch (e) {\n uiLog.error('Save error:', e)\n saveStatus.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to save'}`\n saveStatus.classList.add('error')\n toastError(e instanceof Error ? e.message : 'Failed to save credentials')\n } finally {\n hideBusyUi()\n saveBtn.disabled = false\n saveBtn.textContent = 'Save Credentials'\n }\n}\n", "import { loadCredentialStatus, saveCredentials } from './credentials.js'\nimport { loadConfiguredDevices } from './devices.js'\n// Wire up global event handlers\nimport { discoverDevices } from './discovery.js';\n\n(window as any).loadCredentialStatus = loadCredentialStatus\n;(window as any).saveCredentials = saveCredentials\n;(window as any).discoverDevices = discoverDevices\n\n// Initialize on page load\nasync function init(): Promise<void> {\n await loadCredentialStatus()\n const list = await loadConfiguredDevices()\n}\n\n// Run initialization when DOM is ready\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', init)\n} else {\n init()\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["discoverDevices", "addDevice", "updateDevice", "syncParentPluginConfigFromDisk", "fetchDevices", "renderDeviceList", "addDeviceToConfig", "editDevice", "deleteDeviceFromConfig", "addDeviceToConfig", "saveCredentials", "saveCredentials", "discoverDevices"]
7
+ }
@@ -0,0 +1,21 @@
1
+ import { loadCredentialStatus, saveCredentials } from './credentials.js'
2
+ import { loadConfiguredDevices } from './devices.js'
3
+ // Wire up global event handlers
4
+ import { discoverDevices } from './discovery.js';
5
+
6
+ (window as any).loadCredentialStatus = loadCredentialStatus
7
+ ;(window as any).saveCredentials = saveCredentials
8
+ ;(window as any).discoverDevices = discoverDevices
9
+
10
+ // Initialize on page load
11
+ async function init(): Promise<void> {
12
+ await loadCredentialStatus()
13
+ const list = await loadConfiguredDevices()
14
+ }
15
+
16
+ // Run initialization when DOM is ready
17
+ if (document.readyState === 'loading') {
18
+ document.addEventListener('DOMContentLoaded', init)
19
+ } else {
20
+ init()
21
+ }
@@ -0,0 +1,2 @@
1
+ export { DEVICE_TYPES } from '../../../device-types.js';
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { DEVICE_TYPES } from '../../../device-types.js';
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1 @@
1
+ export { DEVICE_TYPES } from '../../../device-types.js'
@@ -0,0 +1,3 @@
1
+ export declare function loadCredentialStatus(): Promise<void>;
2
+ export declare function saveCredentials(): Promise<void>;
3
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/credentials.ts"],"names":[],"mappings":"AAKA,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgC1D;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CA4DrD"}
@@ -0,0 +1,96 @@
1
+ import { fetchCredentialStatus as apiFetchCredentialStatus, saveCredentials as apiSaveCredentials, syncParentPluginConfigFromDisk } from './api.js';
2
+ import { uiLog } from './logger.js';
3
+ import { hideBusyUi, showBusyUi } from './modal.js';
4
+ import { toastError, toastSuccess, toastWarning } from './toast.js';
5
+ export async function loadCredentialStatus() {
6
+ try {
7
+ const creds = await apiFetchCredentialStatus();
8
+ if (!creds) {
9
+ uiLog.error('Failed to load credentials');
10
+ return;
11
+ }
12
+ const tokenStatus = document.getElementById('tokenStatus');
13
+ const secretStatus = document.getElementById('secretStatus');
14
+ if (!tokenStatus || !secretStatus) {
15
+ return;
16
+ }
17
+ if (creds.hasToken) {
18
+ tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`;
19
+ tokenStatus.classList.add('ok');
20
+ }
21
+ else {
22
+ tokenStatus.textContent = 'Not configured';
23
+ tokenStatus.classList.remove('ok');
24
+ }
25
+ if (creds.hasSecret) {
26
+ secretStatus.textContent = `✓ Configured (${creds.secretLength} characters)`;
27
+ secretStatus.classList.add('ok');
28
+ }
29
+ else {
30
+ secretStatus.textContent = 'Not configured';
31
+ secretStatus.classList.remove('ok');
32
+ }
33
+ }
34
+ catch (e) {
35
+ uiLog.error('Error loading credentials:', e);
36
+ }
37
+ }
38
+ export async function saveCredentials() {
39
+ const token = document.getElementById('token')?.value;
40
+ const secret = document.getElementById('secret')?.value;
41
+ const saveStatus = document.getElementById('saveStatus');
42
+ const saveBtn = document.getElementById('saveBtn');
43
+ if (!saveStatus || !saveBtn) {
44
+ return;
45
+ }
46
+ if (!token || !secret) {
47
+ saveStatus.textContent = 'Please enter both token and secret';
48
+ saveStatus.classList.add('error');
49
+ toastWarning('Please enter both token and secret');
50
+ return;
51
+ }
52
+ try {
53
+ showBusyUi();
54
+ saveBtn.disabled = true;
55
+ saveBtn.textContent = 'Saving...';
56
+ uiLog.info('Saving credentials...');
57
+ const result = await apiSaveCredentials(token, secret);
58
+ uiLog.info('Save response:', result);
59
+ const message = result?.message || 'Credentials saved successfully';
60
+ saveStatus.textContent = `✓ ${message}`;
61
+ saveStatus.classList.remove('error');
62
+ saveStatus.classList.add('success-msg');
63
+ toastSuccess(message);
64
+ const synced = await syncParentPluginConfigFromDisk(true);
65
+ if (synced) {
66
+ saveStatus.textContent += ' - Config saved automatically.';
67
+ toastSuccess('Configuration synced and saved automatically');
68
+ }
69
+ else {
70
+ toastWarning('Credentials saved, but configuration sync failed');
71
+ }
72
+ // Clear inputs after successful save
73
+ ;
74
+ document.getElementById('token').value = '';
75
+ document.getElementById('secret').value = '';
76
+ // Reload status to verify save
77
+ setTimeout(() => loadCredentialStatus(), 500);
78
+ // Clear status message
79
+ setTimeout(() => {
80
+ saveStatus.textContent = '';
81
+ saveStatus.classList.remove('success-msg');
82
+ }, 3000);
83
+ }
84
+ catch (e) {
85
+ uiLog.error('Save error:', e);
86
+ saveStatus.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to save'}`;
87
+ saveStatus.classList.add('error');
88
+ toastError(e instanceof Error ? e.message : 'Failed to save credentials');
89
+ }
90
+ finally {
91
+ hideBusyUi();
92
+ saveBtn.disabled = false;
93
+ saveBtn.textContent = 'Save Credentials';
94
+ }
95
+ }
96
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ import { fetchCredentialStatus as apiFetchCredentialStatus, saveCredentials as apiSaveCredentials, syncParentPluginConfigFromDisk } from './api.js'
2
+ import { uiLog } from './logger.js'
3
+ import { hideBusyUi, showBusyUi } from './modal.js'
4
+ import { toastError, toastSuccess, toastWarning } from './toast.js'
5
+
6
+ export async function loadCredentialStatus(): Promise<void> {
7
+ try {
8
+ const creds = await apiFetchCredentialStatus()
9
+
10
+ if (!creds) {
11
+ uiLog.error('Failed to load credentials')
12
+ return
13
+ }
14
+
15
+ const tokenStatus = document.getElementById('tokenStatus')
16
+ const secretStatus = document.getElementById('secretStatus')
17
+
18
+ if (!tokenStatus || !secretStatus) { return }
19
+
20
+ if (creds.hasToken) {
21
+ tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`
22
+ tokenStatus.classList.add('ok')
23
+ } else {
24
+ tokenStatus.textContent = 'Not configured'
25
+ tokenStatus.classList.remove('ok')
26
+ }
27
+
28
+ if (creds.hasSecret) {
29
+ secretStatus.textContent = `✓ Configured (${creds.secretLength} characters)`
30
+ secretStatus.classList.add('ok')
31
+ } else {
32
+ secretStatus.textContent = 'Not configured'
33
+ secretStatus.classList.remove('ok')
34
+ }
35
+ } catch (e) {
36
+ uiLog.error('Error loading credentials:', e)
37
+ }
38
+ }
39
+
40
+ export async function saveCredentials(): Promise<void> {
41
+ const token = (document.getElementById('token') as HTMLInputElement)?.value
42
+ const secret = (document.getElementById('secret') as HTMLInputElement)?.value
43
+ const saveStatus = document.getElementById('saveStatus')
44
+ const saveBtn = document.getElementById('saveBtn') as HTMLButtonElement
45
+
46
+ if (!saveStatus || !saveBtn) { return }
47
+
48
+ if (!token || !secret) {
49
+ saveStatus.textContent = 'Please enter both token and secret'
50
+ saveStatus.classList.add('error')
51
+ toastWarning('Please enter both token and secret')
52
+ return
53
+ }
54
+
55
+ try {
56
+ showBusyUi()
57
+ saveBtn.disabled = true
58
+ saveBtn.textContent = 'Saving...'
59
+ uiLog.info('Saving credentials...')
60
+
61
+ const result = await apiSaveCredentials(token, secret)
62
+ uiLog.info('Save response:', result)
63
+
64
+ const message = result?.message || 'Credentials saved successfully'
65
+ saveStatus.textContent = `✓ ${message}`
66
+ saveStatus.classList.remove('error')
67
+ saveStatus.classList.add('success-msg')
68
+ toastSuccess(message)
69
+
70
+ const synced = await syncParentPluginConfigFromDisk(true)
71
+ if (synced) {
72
+ saveStatus.textContent += ' - Config saved automatically.'
73
+ toastSuccess('Configuration synced and saved automatically')
74
+ } else {
75
+ toastWarning('Credentials saved, but configuration sync failed')
76
+ }
77
+
78
+ // Clear inputs after successful save
79
+ ;(document.getElementById('token') as HTMLInputElement).value = ''
80
+ ;(document.getElementById('secret') as HTMLInputElement).value = ''
81
+
82
+ // Reload status to verify save
83
+ setTimeout(() => loadCredentialStatus(), 500)
84
+
85
+ // Clear status message
86
+ setTimeout(() => {
87
+ saveStatus.textContent = ''
88
+ saveStatus.classList.remove('success-msg')
89
+ }, 3000)
90
+ } catch (e) {
91
+ uiLog.error('Save error:', e)
92
+ saveStatus.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to save'}`
93
+ saveStatus.classList.add('error')
94
+ toastError(e instanceof Error ? e.message : 'Failed to save credentials')
95
+ } finally {
96
+ hideBusyUi()
97
+ saveBtn.disabled = false
98
+ saveBtn.textContent = 'Save Credentials'
99
+ }
100
+ }
@@ -0,0 +1,2 @@
1
+ export declare function deleteDeviceFromConfig(deviceId: string, deviceName: string): Promise<void>;
2
+ //# sourceMappingURL=devices-delete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices-delete.d.ts","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/devices-delete.ts"],"names":[],"mappings":"AAyEA,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChG"}
@@ -0,0 +1,99 @@
1
+ import { deleteDevice as apiDeleteDevice, syncParentPluginConfigFromDisk, fetchDevices } from './api.js';
2
+ import { uiLog } from './logger.js';
3
+ import { hideBusyUi, showBusyUi } from './modal.js';
4
+ import { renderDeviceList } from './render.js';
5
+ import { toastError, toastSuccess, toastWarning } from './toast.js';
6
+ async function confirmDeleteDialog(deviceNameOrId) {
7
+ return new Promise((resolve) => {
8
+ const overlay = document.createElement('div');
9
+ overlay.style.position = 'fixed';
10
+ overlay.style.top = '0';
11
+ overlay.style.left = '0';
12
+ overlay.style.width = '100%';
13
+ overlay.style.height = '100%';
14
+ overlay.style.background = 'rgba(0,0,0,0.6)';
15
+ overlay.style.display = 'flex';
16
+ overlay.style.alignItems = 'center';
17
+ overlay.style.justifyContent = 'center';
18
+ overlay.style.zIndex = '10000';
19
+ const modal = document.createElement('div');
20
+ modal.style.background = getComputedStyle(document.body).backgroundColor;
21
+ modal.style.color = getComputedStyle(document.body).color;
22
+ modal.style.padding = '20px';
23
+ modal.style.borderRadius = '10px';
24
+ modal.style.minWidth = '340px';
25
+ modal.style.maxWidth = '90vw';
26
+ modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)';
27
+ const title = document.createElement('h3');
28
+ title.textContent = 'Delete Device';
29
+ title.style.margin = '0 0 10px 0';
30
+ const message = document.createElement('p');
31
+ message.textContent = `Remove "${deviceNameOrId}" from configuration?`;
32
+ message.style.margin = '0 0 16px 0';
33
+ const actions = document.createElement('div');
34
+ actions.style.display = 'flex';
35
+ actions.style.justifyContent = 'flex-end';
36
+ actions.style.gap = '8px';
37
+ const cancelBtn = document.createElement('button');
38
+ cancelBtn.textContent = 'Cancel';
39
+ cancelBtn.style.background = '#6b7280';
40
+ const deleteBtn = document.createElement('button');
41
+ deleteBtn.textContent = 'Delete';
42
+ deleteBtn.style.background = '#ef4444';
43
+ const cleanup = (result) => {
44
+ overlay.remove();
45
+ resolve(result);
46
+ };
47
+ cancelBtn.onclick = () => cleanup(false);
48
+ deleteBtn.onclick = () => cleanup(true);
49
+ overlay.addEventListener('click', (event) => {
50
+ if (event.target === overlay) {
51
+ cleanup(false);
52
+ }
53
+ });
54
+ actions.appendChild(cancelBtn);
55
+ actions.appendChild(deleteBtn);
56
+ modal.appendChild(title);
57
+ modal.appendChild(message);
58
+ modal.appendChild(actions);
59
+ overlay.appendChild(modal);
60
+ document.body.appendChild(overlay);
61
+ });
62
+ }
63
+ export async function deleteDeviceFromConfig(deviceId, deviceName) {
64
+ uiLog.info('Delete button clicked for device:', deviceId, deviceName);
65
+ const confirmed = await confirmDeleteDialog(deviceName || deviceId);
66
+ if (!confirmed) {
67
+ uiLog.info('Delete cancelled by user');
68
+ return;
69
+ }
70
+ try {
71
+ showBusyUi();
72
+ uiLog.info('Sending delete request for deviceId:', deviceId);
73
+ const resp = await apiDeleteDevice(deviceId);
74
+ uiLog.info('Delete response:', resp);
75
+ if (!resp || resp.success === false) {
76
+ throw new Error(resp?.data?.message || 'Failed to delete device');
77
+ }
78
+ uiLog.info('Syncing parent config from disk...');
79
+ const synced = await syncParentPluginConfigFromDisk(true);
80
+ if (!synced) {
81
+ toastWarning('Device deleted, but configuration sync failed');
82
+ }
83
+ uiLog.info('Refreshing device list...');
84
+ // Refresh device list
85
+ const list = await fetchDevices();
86
+ uiLog.info('Rendering devices:', list.length);
87
+ renderDeviceList(list);
88
+ uiLog.info('✓ Device deleted successfully');
89
+ toastSuccess(`Device "${deviceName || deviceId}" deleted successfully`);
90
+ }
91
+ catch (e) {
92
+ uiLog.error('Delete error:', e);
93
+ toastError(e instanceof Error ? e.message : 'Failed to delete device');
94
+ }
95
+ finally {
96
+ hideBusyUi();
97
+ }
98
+ }
99
+ //# sourceMappingURL=devices-delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices-delete.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/devices-delete.ts"],"names":[],"mappings":"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"}