@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,132 @@
1
+ import { DEVICE_TYPES } from './constants.js';
2
+ import { uiLog } from './logger.js';
3
+ export async function editDevice(device) {
4
+ // Create modal dialog for editing
5
+ const div = document.createElement('div');
6
+ div.style.position = 'fixed';
7
+ div.style.top = '0';
8
+ div.style.left = '0';
9
+ div.style.width = '100%';
10
+ div.style.height = '100%';
11
+ div.style.background = 'rgba(0,0,0,0.7)';
12
+ div.style.display = 'flex';
13
+ div.style.alignItems = 'center';
14
+ div.style.justifyContent = 'center';
15
+ div.style.zIndex = '9999';
16
+ const modal = document.createElement('div');
17
+ modal.style.background = getComputedStyle(document.body).backgroundColor;
18
+ modal.style.color = getComputedStyle(document.body).color;
19
+ modal.style.padding = '24px';
20
+ modal.style.borderRadius = '8px';
21
+ modal.style.minWidth = '400px';
22
+ modal.style.boxShadow = '0 10px 40px rgba(0,0,0,0.3)';
23
+ const title = document.createElement('h3');
24
+ title.textContent = 'Edit Device';
25
+ title.style.marginTop = '0';
26
+ const nameLabel = document.createElement('label');
27
+ nameLabel.textContent = 'Device Name';
28
+ nameLabel.style.display = 'block';
29
+ nameLabel.style.marginBottom = '8px';
30
+ nameLabel.style.fontWeight = '500';
31
+ const nameInput = document.createElement('input');
32
+ nameInput.type = 'text';
33
+ nameInput.value = device.name || device.id;
34
+ nameInput.style.width = '100%';
35
+ nameInput.style.marginBottom = '16px';
36
+ // Read-only API device type field
37
+ const apiTypeLabel = document.createElement('label');
38
+ apiTypeLabel.textContent = 'Device Type (API - Read Only)';
39
+ apiTypeLabel.style.display = 'block';
40
+ apiTypeLabel.style.marginBottom = '8px';
41
+ apiTypeLabel.style.fontWeight = '500';
42
+ const apiTypeInput = document.createElement('input');
43
+ apiTypeInput.type = 'text';
44
+ apiTypeInput.value = device.deviceType || device.type || 'Unknown';
45
+ apiTypeInput.readOnly = true;
46
+ apiTypeInput.style.width = '100%';
47
+ apiTypeInput.style.marginBottom = '16px';
48
+ apiTypeInput.style.opacity = '0.6';
49
+ apiTypeInput.style.cursor = 'not-allowed';
50
+ // Editable config device type dropdown
51
+ const typeLabel = document.createElement('label');
52
+ typeLabel.textContent = 'Config Device Type (Override)';
53
+ typeLabel.style.display = 'block';
54
+ typeLabel.style.marginBottom = '8px';
55
+ typeLabel.style.fontWeight = '500';
56
+ const typeSelect = document.createElement('select');
57
+ typeSelect.style.width = '100%';
58
+ typeSelect.style.padding = '8px';
59
+ typeSelect.style.marginBottom = '16px';
60
+ typeSelect.style.borderRadius = '4px';
61
+ typeSelect.style.background = getComputedStyle(nameInput).background;
62
+ typeSelect.style.color = getComputedStyle(nameInput).color;
63
+ typeSelect.style.border = getComputedStyle(nameInput).border;
64
+ // Add option groups with all API device types
65
+ Object.keys(DEVICE_TYPES).forEach((categoryName) => {
66
+ const optgroup = document.createElement('optgroup');
67
+ optgroup.label = categoryName;
68
+ DEVICE_TYPES[categoryName].forEach((deviceType) => {
69
+ const opt = document.createElement('option');
70
+ opt.value = deviceType;
71
+ opt.text = deviceType;
72
+ // Select current configDeviceType if set, otherwise match API deviceType
73
+ const currentType = device.configDeviceType || device.deviceType || device.type || '';
74
+ opt.selected = currentType === deviceType;
75
+ optgroup.appendChild(opt);
76
+ });
77
+ typeSelect.appendChild(optgroup);
78
+ });
79
+ const buttons = document.createElement('div');
80
+ buttons.style.display = 'flex';
81
+ buttons.style.gap = '8px';
82
+ buttons.style.justifyContent = 'flex-end';
83
+ const errorMessage = document.createElement('div');
84
+ errorMessage.style.color = '#ef4444';
85
+ errorMessage.style.marginBottom = '12px';
86
+ errorMessage.style.fontSize = '12px';
87
+ errorMessage.style.display = 'none';
88
+ const cancelBtn = document.createElement('button');
89
+ cancelBtn.textContent = 'Cancel';
90
+ cancelBtn.style.background = '#666';
91
+ cancelBtn.onclick = () => div.remove();
92
+ const saveBtn = document.createElement('button');
93
+ saveBtn.textContent = 'Save';
94
+ saveBtn.onclick = async () => {
95
+ try {
96
+ const { updateDevice, syncParentPluginConfigFromDisk, fetchDevices } = await import('./api.js');
97
+ const { renderDeviceList } = await import('./render.js');
98
+ const params = {
99
+ deviceId: device.id,
100
+ configDeviceName: nameInput.value || undefined,
101
+ configDeviceType: typeSelect.value,
102
+ };
103
+ await updateDevice(params.deviceId, params.configDeviceName, params.configDeviceType);
104
+ await syncParentPluginConfigFromDisk();
105
+ // Refresh device list
106
+ uiLog.info('[Edit Device] Refreshing device list after update');
107
+ const list = await fetchDevices();
108
+ renderDeviceList(list);
109
+ div.remove();
110
+ }
111
+ catch (e) {
112
+ uiLog.error('Update error:', e);
113
+ errorMessage.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to update device'}`;
114
+ errorMessage.style.display = 'block';
115
+ }
116
+ };
117
+ buttons.appendChild(cancelBtn);
118
+ buttons.appendChild(saveBtn);
119
+ modal.appendChild(title);
120
+ modal.appendChild(nameLabel);
121
+ modal.appendChild(nameInput);
122
+ modal.appendChild(apiTypeLabel);
123
+ modal.appendChild(apiTypeInput);
124
+ modal.appendChild(typeLabel);
125
+ modal.appendChild(typeSelect);
126
+ modal.appendChild(errorMessage);
127
+ modal.appendChild(buttons);
128
+ div.appendChild(modal);
129
+ document.body.appendChild(div);
130
+ nameInput.focus();
131
+ }
132
+ //# sourceMappingURL=modals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modals.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/modals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ import { DEVICE_TYPES } from './constants.js'
2
+ import { uiLog } from './logger.js'
3
+
4
+ export async function editDevice(device: any): Promise<void> {
5
+ // Create modal dialog for editing
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 = '24px'
22
+ modal.style.borderRadius = '8px'
23
+ modal.style.minWidth = '400px'
24
+ modal.style.boxShadow = '0 10px 40px rgba(0,0,0,0.3)'
25
+
26
+ const title = document.createElement('h3')
27
+ title.textContent = 'Edit Device'
28
+ title.style.marginTop = '0'
29
+
30
+ const nameLabel = document.createElement('label')
31
+ nameLabel.textContent = 'Device Name'
32
+ nameLabel.style.display = 'block'
33
+ nameLabel.style.marginBottom = '8px'
34
+ nameLabel.style.fontWeight = '500'
35
+
36
+ const nameInput = document.createElement('input')
37
+ nameInput.type = 'text'
38
+ nameInput.value = device.name || device.id
39
+ nameInput.style.width = '100%'
40
+ nameInput.style.marginBottom = '16px'
41
+
42
+ // Read-only API device type field
43
+ const apiTypeLabel = document.createElement('label')
44
+ apiTypeLabel.textContent = 'Device Type (API - Read Only)'
45
+ apiTypeLabel.style.display = 'block'
46
+ apiTypeLabel.style.marginBottom = '8px'
47
+ apiTypeLabel.style.fontWeight = '500'
48
+
49
+ const apiTypeInput = document.createElement('input')
50
+ apiTypeInput.type = 'text'
51
+ apiTypeInput.value = device.deviceType || device.type || 'Unknown'
52
+ apiTypeInput.readOnly = true
53
+ apiTypeInput.style.width = '100%'
54
+ apiTypeInput.style.marginBottom = '16px'
55
+ apiTypeInput.style.opacity = '0.6'
56
+ apiTypeInput.style.cursor = 'not-allowed'
57
+
58
+ // Editable config device type dropdown
59
+ const typeLabel = document.createElement('label')
60
+ typeLabel.textContent = 'Config Device Type (Override)'
61
+ typeLabel.style.display = 'block'
62
+ typeLabel.style.marginBottom = '8px'
63
+ typeLabel.style.fontWeight = '500'
64
+
65
+ const typeSelect = document.createElement('select')
66
+ typeSelect.style.width = '100%'
67
+ typeSelect.style.padding = '8px'
68
+ typeSelect.style.marginBottom = '16px'
69
+ typeSelect.style.borderRadius = '4px'
70
+ typeSelect.style.background = getComputedStyle(nameInput).background
71
+ typeSelect.style.color = getComputedStyle(nameInput).color
72
+ typeSelect.style.border = getComputedStyle(nameInput).border
73
+
74
+ // Add option groups with all API device types
75
+ Object.keys(DEVICE_TYPES).forEach((categoryName) => {
76
+ const optgroup = document.createElement('optgroup')
77
+ optgroup.label = categoryName
78
+
79
+ DEVICE_TYPES[categoryName as keyof typeof DEVICE_TYPES].forEach((deviceType) => {
80
+ const opt = document.createElement('option')
81
+ opt.value = deviceType
82
+ opt.text = deviceType
83
+
84
+ // Select current configDeviceType if set, otherwise match API deviceType
85
+ const currentType = device.configDeviceType || device.deviceType || device.type || ''
86
+ opt.selected = currentType === deviceType
87
+ optgroup.appendChild(opt)
88
+ })
89
+
90
+ typeSelect.appendChild(optgroup)
91
+ })
92
+
93
+ const buttons = document.createElement('div')
94
+ buttons.style.display = 'flex'
95
+ buttons.style.gap = '8px'
96
+ buttons.style.justifyContent = 'flex-end'
97
+
98
+ const errorMessage = document.createElement('div')
99
+ errorMessage.style.color = '#ef4444'
100
+ errorMessage.style.marginBottom = '12px'
101
+ errorMessage.style.fontSize = '12px'
102
+ errorMessage.style.display = 'none'
103
+
104
+ const cancelBtn = document.createElement('button')
105
+ cancelBtn.textContent = 'Cancel'
106
+ cancelBtn.style.background = '#666'
107
+ cancelBtn.onclick = () => div.remove()
108
+
109
+ const saveBtn = document.createElement('button')
110
+ saveBtn.textContent = 'Save'
111
+ saveBtn.onclick = async () => {
112
+ try {
113
+ const { updateDevice, syncParentPluginConfigFromDisk, fetchDevices } = await import('./api.js')
114
+ const { renderDeviceList } = await import('./render.js')
115
+
116
+ const params = {
117
+ deviceId: device.id,
118
+ configDeviceName: nameInput.value || undefined,
119
+ configDeviceType: typeSelect.value,
120
+ }
121
+
122
+ await updateDevice(params.deviceId, params.configDeviceName, params.configDeviceType)
123
+ await syncParentPluginConfigFromDisk()
124
+
125
+ // Refresh device list
126
+ uiLog.info('[Edit Device] Refreshing device list after update')
127
+ const list = await fetchDevices()
128
+ renderDeviceList(list)
129
+ div.remove()
130
+ } catch (e) {
131
+ uiLog.error('Update error:', e)
132
+ errorMessage.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to update device'}`
133
+ errorMessage.style.display = 'block'
134
+ }
135
+ }
136
+
137
+ buttons.appendChild(cancelBtn)
138
+ buttons.appendChild(saveBtn)
139
+
140
+ modal.appendChild(title)
141
+ modal.appendChild(nameLabel)
142
+ modal.appendChild(nameInput)
143
+ modal.appendChild(apiTypeLabel)
144
+ modal.appendChild(apiTypeInput)
145
+ modal.appendChild(typeLabel)
146
+ modal.appendChild(typeSelect)
147
+ modal.appendChild(errorMessage)
148
+ modal.appendChild(buttons)
149
+
150
+ div.appendChild(modal)
151
+ document.body.appendChild(div)
152
+ nameInput.focus()
153
+ }
@@ -0,0 +1,6 @@
1
+ export declare function renderBadge(text: string, style: string): HTMLElement;
2
+ export declare function renderConnectionBadge(connectionType: string): HTMLElement | null;
3
+ export declare function renderIRBadge(): HTMLElement;
4
+ export declare function renderDiscoveredDevices(devices: any[]): Promise<HTMLElement>;
5
+ export declare function renderDeviceList(list: any[]): void;
6
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/render.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAKpE;AAED,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAiBhF;AAED,wBAAgB,aAAa,IAAI,WAAW,CAK3C;AAED,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAyDlF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CA+FlD"}
@@ -0,0 +1,166 @@
1
+ export function renderBadge(text, style) {
2
+ const badge = document.createElement('span');
3
+ badge.textContent = text;
4
+ badge.style.cssText = style;
5
+ return badge;
6
+ }
7
+ export function renderConnectionBadge(connectionType) {
8
+ if (!connectionType) {
9
+ return null;
10
+ }
11
+ const badge = renderBadge(connectionType, '');
12
+ if (connectionType === 'BLE') {
13
+ badge.style.cssText
14
+ = 'background: #4285f4; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;';
15
+ }
16
+ else if (connectionType === 'Both') {
17
+ badge.style.cssText
18
+ = 'background: #34a853; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;';
19
+ }
20
+ else {
21
+ badge.style.cssText
22
+ = 'background: #9e9e9e; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;';
23
+ }
24
+ return badge;
25
+ }
26
+ export function renderIRBadge() {
27
+ return renderBadge('IR', 'background: #ff6b35; color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; margin-left: 8px;');
28
+ }
29
+ export async function renderDiscoveredDevices(devices) {
30
+ const ul = document.createElement('ul');
31
+ ul.style.maxHeight = '300px';
32
+ ul.style.overflowY = 'auto';
33
+ const { addDeviceToConfig } = await import('./discovery.js');
34
+ for (const d of devices) {
35
+ const li = document.createElement('li');
36
+ li.className = 'device-item';
37
+ const info = document.createElement('div');
38
+ info.style.flex = '1';
39
+ const name = document.createElement('div');
40
+ name.style.fontWeight = '500';
41
+ name.textContent = d.name || d.id;
42
+ // Add connection type badge
43
+ if (d.connectionType) {
44
+ name.appendChild(document.createTextNode(' '));
45
+ const badge = renderConnectionBadge(d.connectionType);
46
+ if (badge) {
47
+ name.appendChild(badge);
48
+ }
49
+ }
50
+ // Add IR badge if it's an IR device
51
+ if (d.isIR) {
52
+ name.appendChild(document.createTextNode(' '));
53
+ name.appendChild(renderIRBadge());
54
+ }
55
+ const details = document.createElement('div');
56
+ details.style.fontSize = '12px';
57
+ details.style.opacity = '0.75';
58
+ let detailsText = `ID: ${d.id} | Type: ${d.type} | Model: ${d.model || 'N/A'}`;
59
+ if (d.hubDeviceId) {
60
+ detailsText += ` | Hub: ${d.hubDeviceId}`;
61
+ }
62
+ if (d.rssi) {
63
+ detailsText += ` | Signal: ${d.rssi} dBm`;
64
+ }
65
+ if (d.address) {
66
+ detailsText += ` | MAC: ${d.address}`;
67
+ }
68
+ details.textContent = detailsText;
69
+ info.appendChild(name);
70
+ info.appendChild(details);
71
+ const addBtn = document.createElement('button');
72
+ addBtn.textContent = '+ Add';
73
+ addBtn.style.marginLeft = '8px';
74
+ addBtn.onclick = async () => {
75
+ await addDeviceToConfig(d);
76
+ };
77
+ li.appendChild(info);
78
+ li.appendChild(addBtn);
79
+ ul.appendChild(li);
80
+ }
81
+ return ul;
82
+ }
83
+ export function renderDeviceList(list) {
84
+ const ul = document.getElementById('devices');
85
+ const status = document.getElementById('status');
86
+ if (!ul || !status) {
87
+ return;
88
+ }
89
+ if (!list.length) {
90
+ status.textContent = 'No devices found in config.';
91
+ ul.innerHTML = '';
92
+ return;
93
+ }
94
+ status.textContent = `Found ${list.length} device(s)`;
95
+ ul.innerHTML = '';
96
+ for (const d of list) {
97
+ const li = document.createElement('li');
98
+ li.className = 'device-item';
99
+ const info = document.createElement('div');
100
+ info.style.flex = '1';
101
+ const name = document.createElement('div');
102
+ name.style.fontWeight = '500';
103
+ name.textContent = d.name || d.id;
104
+ const code = document.createElement('code');
105
+ code.textContent = d.id;
106
+ code.style.fontSize = '12px';
107
+ code.style.opacity = '0.75';
108
+ code.style.marginLeft = '8px';
109
+ const meta = document.createElement('div');
110
+ meta.style.opacity = '0.75';
111
+ meta.style.marginTop = '4px';
112
+ meta.style.fontSize = '12px';
113
+ const typeText = d.type ? `type: ${d.type}` : '';
114
+ const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : '';
115
+ const roomText = d.room ? `room: ${d.room}` : '';
116
+ meta.textContent = [typeText, connText, roomText].filter(Boolean).join(' | ');
117
+ info.appendChild(name);
118
+ info.appendChild(code);
119
+ info.appendChild(meta);
120
+ const buttons = document.createElement('div');
121
+ buttons.style.display = 'flex';
122
+ buttons.style.gap = '8px';
123
+ const editBtn = document.createElement('button');
124
+ editBtn.textContent = '✏️ Edit';
125
+ editBtn.style.padding = '6px 12px';
126
+ editBtn.style.fontSize = '12px';
127
+ editBtn.onclick = async () => {
128
+ const { editDevice } = await import('./modals.js');
129
+ await editDevice(d);
130
+ };
131
+ const copyBtn = document.createElement('button');
132
+ copyBtn.textContent = 'Copy ID';
133
+ copyBtn.style.padding = '6px 12px';
134
+ copyBtn.style.fontSize = '12px';
135
+ copyBtn.addEventListener('click', async () => {
136
+ try {
137
+ await navigator.clipboard.writeText(d.id);
138
+ copyBtn.textContent = 'Copied';
139
+ copyBtn.classList.add('success');
140
+ setTimeout(() => {
141
+ copyBtn.textContent = 'Copy ID';
142
+ copyBtn.classList.remove('success');
143
+ }, 1200);
144
+ }
145
+ catch (e) {
146
+ alert('Failed to copy');
147
+ }
148
+ });
149
+ const deleteBtn = document.createElement('button');
150
+ deleteBtn.textContent = '🗑️ Delete';
151
+ deleteBtn.style.padding = '6px 12px';
152
+ deleteBtn.style.fontSize = '12px';
153
+ deleteBtn.style.background = '#ef4444';
154
+ deleteBtn.onclick = async () => {
155
+ const { deleteDeviceFromConfig } = await import('./devices-delete.js');
156
+ await deleteDeviceFromConfig(d.id, d.name || d.id);
157
+ };
158
+ buttons.appendChild(editBtn);
159
+ buttons.appendChild(copyBtn);
160
+ buttons.appendChild(deleteBtn);
161
+ li.appendChild(info);
162
+ li.appendChild(buttons);
163
+ ul.appendChild(li);
164
+ }
165
+ }
166
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/render.ts"],"names":[],"mappings":"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"}