@switchbot/homebridge-switchbot 5.0.0-beta.109 → 5.0.0-beta.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.schema.json +4 -0
- package/dist/device-types.d.ts +18 -2
- package/dist/device-types.d.ts.map +1 -1
- package/dist/device-types.js +71 -0
- package/dist/device-types.js.map +1 -1
- package/dist/deviceFactory.d.ts.map +1 -1
- package/dist/deviceFactory.js +1 -0
- package/dist/deviceFactory.js.map +1 -1
- package/dist/homebridge-ui/endpoints/config.d.ts +0 -3
- package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/config.js +40 -9
- package/dist/homebridge-ui/endpoints/config.js.map +1 -1
- package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/devices.js +59 -2
- package/dist/homebridge-ui/endpoints/devices.js.map +1 -1
- package/dist/homebridge-ui/public/css/styles.css +254 -0
- package/dist/homebridge-ui/public/index.html +23 -168
- package/dist/homebridge-ui/public/js/api.d.ts +4 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +14 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +17 -1
- package/dist/homebridge-ui/public/js/app.js +556 -44
- package/dist/homebridge-ui/public/js/app.js.map +3 -3
- package/dist/homebridge-ui/public/js/app.ts +2 -1
- package/dist/homebridge-ui/public/js/devices-delete.d.ts +1 -0
- package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices-delete.js +108 -1
- package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices-delete.ts +124 -1
- package/dist/homebridge-ui/public/js/devices.d.ts +1 -0
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices.js +10 -0
- package/dist/homebridge-ui/public/js/devices.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices.ts +10 -0
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.js +130 -4
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.ts +158 -4
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/modals.js +71 -26
- package/dist/homebridge-ui/public/js/modals.js.map +1 -1
- package/dist/homebridge-ui/public/js/modals.ts +74 -27
- package/dist/homebridge-ui/public/js/render.d.ts +57 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +286 -13
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +331 -11
- package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
- package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/device-migration.js +111 -0
- package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
- package/dist/platform.js +2 -2
- package/dist/platform.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +1 -1
- package/src/device-types.ts +85 -0
- package/src/deviceFactory.ts +1 -0
- package/src/homebridge-ui/endpoints/config.ts +54 -10
- package/src/homebridge-ui/endpoints/devices.ts +62 -3
- package/src/homebridge-ui/public/css/styles.css +254 -0
- package/src/homebridge-ui/public/index.html +23 -168
- package/src/homebridge-ui/public/js/api.ts +17 -1
- package/src/homebridge-ui/public/js/app.ts +2 -1
- package/src/homebridge-ui/public/js/devices-delete.ts +124 -1
- package/src/homebridge-ui/public/js/devices.ts +10 -0
- package/src/homebridge-ui/public/js/discovery.ts +158 -4
- package/src/homebridge-ui/public/js/modals.ts +74 -27
- package/src/homebridge-ui/public/js/render.ts +331 -11
- package/src/homebridge-ui/utils/device-migration.ts +144 -0
- package/src/platform.ts +2 -2
- package/todo/COMPLETED.md +189 -0
- package/todo/README.md +59 -0
- package/todo/TODO.md +205 -0
- package/todo/mockups/filtering-sorting.svg +112 -0
- package/todo/mockups/modal-switchbot-theme.svg +66 -0
- package/todo/mockups/signal-strength-visualization.svg +94 -0
- package/TODO.md +0 -564
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light dark;
|
|
3
|
+
--switchbot-red: #ef4444;
|
|
4
|
+
--switchbot-red-hover: #dc2626;
|
|
5
|
+
--switchbot-red-light: #fee2e2;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@media (prefers-color-scheme: light) {
|
|
9
|
+
body {
|
|
10
|
+
background: #f5f5f5;
|
|
11
|
+
color: #1a1a1a;
|
|
12
|
+
}
|
|
13
|
+
input {
|
|
14
|
+
background: #fff;
|
|
15
|
+
border-color: #ddd;
|
|
16
|
+
color: #1a1a1a;
|
|
17
|
+
}
|
|
18
|
+
input:focus {
|
|
19
|
+
border-color: #6366f1;
|
|
20
|
+
}
|
|
21
|
+
.card {
|
|
22
|
+
background: #fff;
|
|
23
|
+
border: 1px solid #ddd;
|
|
24
|
+
}
|
|
25
|
+
code {
|
|
26
|
+
background: #f0f0f0;
|
|
27
|
+
color: #1a1a1a;
|
|
28
|
+
}
|
|
29
|
+
h2 {
|
|
30
|
+
border-bottom-color: #ddd;
|
|
31
|
+
}
|
|
32
|
+
.status {
|
|
33
|
+
color: #666;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@media (prefers-color-scheme: dark) {
|
|
38
|
+
body {
|
|
39
|
+
background: #1a1a1a;
|
|
40
|
+
color: #fff;
|
|
41
|
+
}
|
|
42
|
+
input {
|
|
43
|
+
background: #2a2a2a;
|
|
44
|
+
border-color: #444;
|
|
45
|
+
color: #fff;
|
|
46
|
+
}
|
|
47
|
+
.card {
|
|
48
|
+
background: #2a2a2a;
|
|
49
|
+
}
|
|
50
|
+
code {
|
|
51
|
+
background: #333;
|
|
52
|
+
color: #fff;
|
|
53
|
+
}
|
|
54
|
+
h2 {
|
|
55
|
+
border-bottom-color: #444;
|
|
56
|
+
}
|
|
57
|
+
.status {
|
|
58
|
+
color: #888;
|
|
59
|
+
}
|
|
60
|
+
.device-item {
|
|
61
|
+
background: #333;
|
|
62
|
+
border: 1px solid #444;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@media (prefers-color-scheme: light) {
|
|
67
|
+
.device-item {
|
|
68
|
+
background: #f9f9f9;
|
|
69
|
+
border: 1px solid #e0e0e0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
body {
|
|
74
|
+
font-family:
|
|
75
|
+
system-ui,
|
|
76
|
+
-apple-system,
|
|
77
|
+
Arial;
|
|
78
|
+
padding: 16px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
h1 {
|
|
82
|
+
font-size: 24px;
|
|
83
|
+
margin-top: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
h2 {
|
|
87
|
+
font-size: 16px;
|
|
88
|
+
margin-top: 24px;
|
|
89
|
+
margin-bottom: 12px;
|
|
90
|
+
border-bottom: 1px solid;
|
|
91
|
+
padding-bottom: 8px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ul {
|
|
95
|
+
padding-left: 0;
|
|
96
|
+
list-style: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
li {
|
|
100
|
+
margin: 8px 0;
|
|
101
|
+
display: flex;
|
|
102
|
+
gap: 8px;
|
|
103
|
+
align-items: center;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
button {
|
|
107
|
+
padding: 10px 20px;
|
|
108
|
+
background: var(--switchbot-red);
|
|
109
|
+
color: #fff;
|
|
110
|
+
border: none;
|
|
111
|
+
border-radius: 6px;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
font-weight: 500;
|
|
114
|
+
font-size: 14px;
|
|
115
|
+
transition: all 0.2s ease;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
button:hover {
|
|
119
|
+
background: var(--switchbot-red-hover);
|
|
120
|
+
transform: translateY(-1px);
|
|
121
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
button:active {
|
|
125
|
+
transform: translateY(0);
|
|
126
|
+
box-shadow: 0 2px 6px rgba(239, 68, 68, 0.2);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
button.secondary {
|
|
130
|
+
background: #6b7280;
|
|
131
|
+
color: #fff;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
button.secondary:hover {
|
|
135
|
+
background: #4b5563;
|
|
136
|
+
box-shadow: 0 4px 12px rgba(107, 114, 128, 0.3);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
button.success {
|
|
140
|
+
background: #10b981;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
button.success:hover {
|
|
144
|
+
background: #059669;
|
|
145
|
+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
code {
|
|
149
|
+
padding: 4px 6px;
|
|
150
|
+
border-radius: 4px;
|
|
151
|
+
font-family: monospace;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.form-group {
|
|
155
|
+
margin-bottom: 16px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
label {
|
|
159
|
+
display: block;
|
|
160
|
+
margin-bottom: 6px;
|
|
161
|
+
font-weight: 500;
|
|
162
|
+
font-size: 12px;
|
|
163
|
+
color: #6b7280;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
input {
|
|
167
|
+
width: 100%;
|
|
168
|
+
max-width: 400px;
|
|
169
|
+
padding: 8px 10px;
|
|
170
|
+
border: 1px solid #d1d5db;
|
|
171
|
+
border-radius: 6px;
|
|
172
|
+
font-family: system-ui, -apple-system, monospace;
|
|
173
|
+
font-size: 14px;
|
|
174
|
+
transition: all 0.2s ease;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
input:focus {
|
|
178
|
+
outline: none;
|
|
179
|
+
border-color: var(--switchbot-red, #ef4444);
|
|
180
|
+
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
input:disabled,
|
|
184
|
+
input[readonly] {
|
|
185
|
+
background-color: #f9fafb;
|
|
186
|
+
cursor: not-allowed;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
select {
|
|
190
|
+
padding: 8px 10px;
|
|
191
|
+
border: 1px solid #d1d5db;
|
|
192
|
+
border-radius: 6px;
|
|
193
|
+
font-family: system-ui, -apple-system, Arial;
|
|
194
|
+
font-size: 14px;
|
|
195
|
+
transition: all 0.2s ease;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
select:focus {
|
|
200
|
+
outline: none;
|
|
201
|
+
border-color: var(--switchbot-red, #ef4444);
|
|
202
|
+
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.status {
|
|
206
|
+
font-size: 14px;
|
|
207
|
+
margin-top: 4px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.status.ok {
|
|
211
|
+
color: #10b981;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.error {
|
|
215
|
+
color: #ef4444;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.success-msg {
|
|
219
|
+
color: #10b981;
|
|
220
|
+
margin-top: 8px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.card {
|
|
224
|
+
padding: 16px;
|
|
225
|
+
border-radius: 8px;
|
|
226
|
+
margin-bottom: 16px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.device-item {
|
|
230
|
+
padding: 10px 12px;
|
|
231
|
+
border-radius: 6px;
|
|
232
|
+
margin-bottom: 6px;
|
|
233
|
+
display: flex;
|
|
234
|
+
gap: 10px;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: space-between;
|
|
237
|
+
transition: all 0.2s ease;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.device-item:hover {
|
|
241
|
+
transform: translateY(-1px);
|
|
242
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.card {
|
|
246
|
+
padding: 16px;
|
|
247
|
+
border-radius: 8px;
|
|
248
|
+
margin-bottom: 16px;
|
|
249
|
+
transition: box-shadow 0.2s ease;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.card:hover {
|
|
253
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
254
|
+
}
|
|
@@ -4,174 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
6
|
<title>SwitchBot Plugin UI</title>
|
|
7
|
-
<
|
|
8
|
-
:root {
|
|
9
|
-
color-scheme: light dark;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
@media (prefers-color-scheme: light) {
|
|
13
|
-
body {
|
|
14
|
-
background: #f5f5f5;
|
|
15
|
-
color: #1a1a1a;
|
|
16
|
-
}
|
|
17
|
-
input {
|
|
18
|
-
background: #fff;
|
|
19
|
-
border-color: #ddd;
|
|
20
|
-
color: #1a1a1a;
|
|
21
|
-
}
|
|
22
|
-
input:focus {
|
|
23
|
-
border-color: #6366f1;
|
|
24
|
-
}
|
|
25
|
-
.card {
|
|
26
|
-
background: #fff;
|
|
27
|
-
border: 1px solid #ddd;
|
|
28
|
-
}
|
|
29
|
-
code {
|
|
30
|
-
background: #f0f0f0;
|
|
31
|
-
color: #1a1a1a;
|
|
32
|
-
}
|
|
33
|
-
h2 {
|
|
34
|
-
border-bottom-color: #ddd;
|
|
35
|
-
}
|
|
36
|
-
.status {
|
|
37
|
-
color: #666;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@media (prefers-color-scheme: dark) {
|
|
42
|
-
body {
|
|
43
|
-
background: #1a1a1a;
|
|
44
|
-
color: #fff;
|
|
45
|
-
}
|
|
46
|
-
input {
|
|
47
|
-
background: #2a2a2a;
|
|
48
|
-
border-color: #444;
|
|
49
|
-
color: #fff;
|
|
50
|
-
}
|
|
51
|
-
.card {
|
|
52
|
-
background: #2a2a2a;
|
|
53
|
-
}
|
|
54
|
-
code {
|
|
55
|
-
background: #333;
|
|
56
|
-
color: #fff;
|
|
57
|
-
}
|
|
58
|
-
h2 {
|
|
59
|
-
border-bottom-color: #444;
|
|
60
|
-
}
|
|
61
|
-
.status {
|
|
62
|
-
color: #888;
|
|
63
|
-
}
|
|
64
|
-
.device-item {
|
|
65
|
-
background: #333;
|
|
66
|
-
border: 1px solid #444;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
@media (prefers-color-scheme: light) {
|
|
71
|
-
.device-item {
|
|
72
|
-
background: #f9f9f9;
|
|
73
|
-
border: 1px solid #e0e0e0;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@media (prefers-color-scheme: dark) {
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
body {
|
|
81
|
-
font-family:
|
|
82
|
-
system-ui,
|
|
83
|
-
-apple-system,
|
|
84
|
-
Arial;
|
|
85
|
-
padding: 16px;
|
|
86
|
-
}
|
|
87
|
-
h1 {
|
|
88
|
-
font-size: 24px;
|
|
89
|
-
margin-top: 0;
|
|
90
|
-
}
|
|
91
|
-
h2 {
|
|
92
|
-
font-size: 16px;
|
|
93
|
-
margin-top: 24px;
|
|
94
|
-
margin-bottom: 12px;
|
|
95
|
-
border-bottom: 1px solid;
|
|
96
|
-
padding-bottom: 8px;
|
|
97
|
-
}
|
|
98
|
-
ul {
|
|
99
|
-
padding-left: 0;
|
|
100
|
-
list-style: none;
|
|
101
|
-
}
|
|
102
|
-
li {
|
|
103
|
-
margin: 8px 0;
|
|
104
|
-
display: flex;
|
|
105
|
-
gap: 8px;
|
|
106
|
-
align-items: center;
|
|
107
|
-
}
|
|
108
|
-
button {
|
|
109
|
-
padding: 8px 16px;
|
|
110
|
-
background: #6366f1;
|
|
111
|
-
color: #fff;
|
|
112
|
-
border: none;
|
|
113
|
-
border-radius: 4px;
|
|
114
|
-
cursor: pointer;
|
|
115
|
-
}
|
|
116
|
-
button:hover {
|
|
117
|
-
background: #4f46e5;
|
|
118
|
-
}
|
|
119
|
-
button.success {
|
|
120
|
-
background: #10b981;
|
|
121
|
-
}
|
|
122
|
-
code {
|
|
123
|
-
padding: 4px 6px;
|
|
124
|
-
border-radius: 4px;
|
|
125
|
-
font-family: monospace;
|
|
126
|
-
}
|
|
127
|
-
.form-group {
|
|
128
|
-
margin-bottom: 16px;
|
|
129
|
-
}
|
|
130
|
-
label {
|
|
131
|
-
display: block;
|
|
132
|
-
margin-bottom: 6px;
|
|
133
|
-
font-weight: 500;
|
|
134
|
-
}
|
|
135
|
-
input {
|
|
136
|
-
width: 100%;
|
|
137
|
-
max-width: 400px;
|
|
138
|
-
padding: 8px;
|
|
139
|
-
border: 1px solid;
|
|
140
|
-
border-radius: 4px;
|
|
141
|
-
font-family: monospace;
|
|
142
|
-
}
|
|
143
|
-
input:focus {
|
|
144
|
-
outline: none;
|
|
145
|
-
}
|
|
146
|
-
.status {
|
|
147
|
-
font-size: 14px;
|
|
148
|
-
margin-top: 4px;
|
|
149
|
-
}
|
|
150
|
-
.status.ok {
|
|
151
|
-
color: #10b981;
|
|
152
|
-
}
|
|
153
|
-
.error {
|
|
154
|
-
color: #ef4444;
|
|
155
|
-
}
|
|
156
|
-
.success-msg {
|
|
157
|
-
color: #10b981;
|
|
158
|
-
margin-top: 8px;
|
|
159
|
-
}
|
|
160
|
-
.card {
|
|
161
|
-
padding: 16px;
|
|
162
|
-
border-radius: 8px;
|
|
163
|
-
margin-bottom: 16px;
|
|
164
|
-
}
|
|
165
|
-
.device-item {
|
|
166
|
-
padding: 8px;
|
|
167
|
-
border-radius: 4px;
|
|
168
|
-
margin-bottom: 8px;
|
|
169
|
-
display: flex;
|
|
170
|
-
gap: 8px;
|
|
171
|
-
align-items: center;
|
|
172
|
-
justify-content: space-between;
|
|
173
|
-
}
|
|
174
|
-
</style>
|
|
7
|
+
<link rel="stylesheet" href="css/styles.css" />
|
|
175
8
|
</head>
|
|
176
9
|
<body>
|
|
177
10
|
<h1>🤖 SwitchBot Configuration</h1>
|
|
@@ -252,6 +85,28 @@
|
|
|
252
85
|
</p>
|
|
253
86
|
<div id="status">Loading…</div>
|
|
254
87
|
<ul id="devices"></ul>
|
|
88
|
+
<div
|
|
89
|
+
id="removeAllContainer"
|
|
90
|
+
style="
|
|
91
|
+
display: none;
|
|
92
|
+
margin-top: 16px;
|
|
93
|
+
padding-top: 16px;
|
|
94
|
+
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
95
|
+
"
|
|
96
|
+
>
|
|
97
|
+
<button
|
|
98
|
+
id="removeAllBtn"
|
|
99
|
+
style="
|
|
100
|
+
background: #dc2626;
|
|
101
|
+
width: 100%;
|
|
102
|
+
padding: 10px 16px;
|
|
103
|
+
font-size: 14px;
|
|
104
|
+
font-weight: 500;
|
|
105
|
+
"
|
|
106
|
+
>
|
|
107
|
+
🗑️ Remove All Devices
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
255
110
|
</div>
|
|
256
111
|
|
|
257
112
|
<script type="module" src="js/app.js"></script>
|
|
@@ -132,7 +132,7 @@ export async function addDevice(deviceId: string, name: string, type: string): P
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
export async function addDevicesInBulk(
|
|
135
|
-
devices: Array<{ deviceId: string, name: string, type: string }>,
|
|
135
|
+
devices: Array<{ deviceId: string, name: string, type: string, rssi?: number, address?: string, model?: string }>,
|
|
136
136
|
): Promise<any> {
|
|
137
137
|
try {
|
|
138
138
|
const resp = await homebridge.request('/add-devices', { devices })
|
|
@@ -187,3 +187,19 @@ export async function deleteDevice(deviceId: string): Promise<any> {
|
|
|
187
187
|
throw e
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
export async function deleteAllDevices(): Promise<any> {
|
|
192
|
+
try {
|
|
193
|
+
uiLog.info('Sending delete all devices request')
|
|
194
|
+
const resp = await homebridge.request('/delete-all-devices', {})
|
|
195
|
+
uiLog.info('Delete all response:', resp)
|
|
196
|
+
|
|
197
|
+
if (!resp || resp.success === false) {
|
|
198
|
+
throw new Error(resp?.data?.message || 'Failed to delete all devices')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return resp.data || resp
|
|
202
|
+
} catch (e) {
|
|
203
|
+
throw e
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadCredentialStatus, saveCredentials } from './credentials.js'
|
|
2
|
-
import { loadConfiguredDevices } from './devices.js'
|
|
2
|
+
import { loadConfiguredDevices, initRemoveAllButton } from './devices.js'
|
|
3
3
|
// Wire up global event handlers
|
|
4
4
|
import { discoverDevices } from './discovery.js';
|
|
5
5
|
|
|
@@ -11,6 +11,7 @@ import { discoverDevices } from './discovery.js';
|
|
|
11
11
|
async function init(): Promise<void> {
|
|
12
12
|
await loadCredentialStatus()
|
|
13
13
|
const list = await loadConfiguredDevices()
|
|
14
|
+
await initRemoveAllButton()
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
// Run initialization when DOM is ready
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deleteDevice as apiDeleteDevice, syncParentPluginConfigFromDisk, fetchDevices } from './api.js'
|
|
1
|
+
import { deleteDevice as apiDeleteDevice, deleteAllDevices as apiDeleteAllDevices, syncParentPluginConfigFromDisk, fetchDevices } from './api.js'
|
|
2
2
|
import { uiLog } from './logger.js'
|
|
3
3
|
import { hideBusyUi, showBusyUi } from './modal.js'
|
|
4
4
|
import { renderDeviceList } from './render.js'
|
|
@@ -111,3 +111,126 @@ export async function deleteDeviceFromConfig(deviceId: string, deviceName: strin
|
|
|
111
111
|
hideBusyUi()
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
async function confirmDeleteAllDialog(deviceCount: number): Promise<boolean> {
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
const overlay = document.createElement('div')
|
|
118
|
+
overlay.style.position = 'fixed'
|
|
119
|
+
overlay.style.top = '0'
|
|
120
|
+
overlay.style.left = '0'
|
|
121
|
+
overlay.style.width = '100%'
|
|
122
|
+
overlay.style.height = '100%'
|
|
123
|
+
overlay.style.background = 'rgba(0,0,0,0.7)'
|
|
124
|
+
overlay.style.display = 'flex'
|
|
125
|
+
overlay.style.alignItems = 'center'
|
|
126
|
+
overlay.style.justifyContent = 'center'
|
|
127
|
+
overlay.style.zIndex = '10000'
|
|
128
|
+
|
|
129
|
+
const modal = document.createElement('div')
|
|
130
|
+
modal.style.background = getComputedStyle(document.body).backgroundColor
|
|
131
|
+
modal.style.color = getComputedStyle(document.body).color
|
|
132
|
+
modal.style.padding = '20px'
|
|
133
|
+
modal.style.borderRadius = '10px'
|
|
134
|
+
modal.style.minWidth = '380px'
|
|
135
|
+
modal.style.maxWidth = '90vw'
|
|
136
|
+
modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)'
|
|
137
|
+
modal.style.borderTop = '3px solid #ef4444'
|
|
138
|
+
|
|
139
|
+
const title = document.createElement('h3')
|
|
140
|
+
title.textContent = '⚠️ Remove All Devices'
|
|
141
|
+
title.style.margin = '0 0 12px 0'
|
|
142
|
+
title.style.color = '#ef4444'
|
|
143
|
+
|
|
144
|
+
const message = document.createElement('p')
|
|
145
|
+
message.innerHTML = `Are you sure you want to remove <strong>all ${deviceCount} device(s)</strong> from your configuration?<br><br>This action cannot be undone.`
|
|
146
|
+
message.style.margin = '0 0 18px 0'
|
|
147
|
+
message.style.lineHeight = '1.5'
|
|
148
|
+
|
|
149
|
+
const actions = document.createElement('div')
|
|
150
|
+
actions.style.display = 'flex'
|
|
151
|
+
actions.style.justifyContent = 'flex-end'
|
|
152
|
+
actions.style.gap = '10px'
|
|
153
|
+
|
|
154
|
+
const cancelBtn = document.createElement('button')
|
|
155
|
+
cancelBtn.textContent = 'Cancel'
|
|
156
|
+
cancelBtn.style.background = '#6b7280'
|
|
157
|
+
cancelBtn.style.padding = '8px 16px'
|
|
158
|
+
cancelBtn.style.fontSize = '13px'
|
|
159
|
+
cancelBtn.className = 'secondary'
|
|
160
|
+
|
|
161
|
+
const deleteBtn = document.createElement('button')
|
|
162
|
+
deleteBtn.textContent = 'Remove All'
|
|
163
|
+
deleteBtn.style.background = '#ef4444'
|
|
164
|
+
deleteBtn.style.padding = '8px 20px'
|
|
165
|
+
deleteBtn.style.fontSize = '13px'
|
|
166
|
+
|
|
167
|
+
const cleanup = (result: boolean) => {
|
|
168
|
+
overlay.remove()
|
|
169
|
+
resolve(result)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
cancelBtn.onclick = () => cleanup(false)
|
|
173
|
+
deleteBtn.onclick = () => cleanup(true)
|
|
174
|
+
overlay.addEventListener('click', (event) => {
|
|
175
|
+
if (event.target === overlay) {
|
|
176
|
+
cleanup(false)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
actions.appendChild(cancelBtn)
|
|
181
|
+
actions.appendChild(deleteBtn)
|
|
182
|
+
modal.appendChild(title)
|
|
183
|
+
modal.appendChild(message)
|
|
184
|
+
modal.appendChild(actions)
|
|
185
|
+
overlay.appendChild(modal)
|
|
186
|
+
document.body.appendChild(overlay)
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function deleteAllDevicesFromConfig(): Promise<void> {
|
|
191
|
+
uiLog.info('Remove All Devices button clicked')
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
// Get current device count
|
|
195
|
+
const list = await fetchDevices()
|
|
196
|
+
if (!list || list.length === 0) {
|
|
197
|
+
toastWarning('No devices to remove')
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const confirmed = await confirmDeleteAllDialog(list.length)
|
|
202
|
+
if (!confirmed) {
|
|
203
|
+
uiLog.info('Remove all cancelled by user')
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
showBusyUi()
|
|
208
|
+
uiLog.info('Sending delete all devices request')
|
|
209
|
+
const resp = await apiDeleteAllDevices()
|
|
210
|
+
uiLog.info('Delete all response:', resp)
|
|
211
|
+
|
|
212
|
+
if (!resp || resp.success === false) {
|
|
213
|
+
throw new Error(resp?.data?.message || 'Failed to delete all devices')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
uiLog.info('Syncing parent config from disk...')
|
|
217
|
+
const synced = await syncParentPluginConfigFromDisk(true)
|
|
218
|
+
if (!synced) {
|
|
219
|
+
toastWarning('Devices deleted, but configuration sync failed')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
uiLog.info('Refreshing device list...')
|
|
223
|
+
const updatedList = await fetchDevices()
|
|
224
|
+
uiLog.info('Rendering devices:', updatedList.length)
|
|
225
|
+
renderDeviceList(updatedList)
|
|
226
|
+
|
|
227
|
+
const deletedCount = resp?.deletedCount || list.length
|
|
228
|
+
uiLog.info(`✓ Removed ${deletedCount} device(s) successfully`)
|
|
229
|
+
toastSuccess(`Removed ${deletedCount} device(s) successfully`)
|
|
230
|
+
} catch (e) {
|
|
231
|
+
uiLog.error('Delete all error:', e)
|
|
232
|
+
toastError(e instanceof Error ? e.message : 'Failed to delete all devices')
|
|
233
|
+
} finally {
|
|
234
|
+
hideBusyUi()
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -72,6 +72,16 @@ export async function addDeviceToConfig(device: any, options: { refresh?: boolea
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
export async function initRemoveAllButton(): Promise<void> {
|
|
76
|
+
const removeAllBtn = document.getElementById('removeAllBtn')
|
|
77
|
+
if (!removeAllBtn) { return }
|
|
78
|
+
|
|
79
|
+
removeAllBtn.addEventListener('click', async () => {
|
|
80
|
+
const { deleteAllDevicesFromConfig } = await import('./devices-delete.js')
|
|
81
|
+
await deleteAllDevicesFromConfig()
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
export async function loadConfiguredDevices(): Promise<void> {
|
|
76
86
|
const list = await fetchDevices()
|
|
77
87
|
renderDeviceList(list)
|