55 < meta name ="viewport " content ="width=device-width,initial-scale=1 " />
66 < title > SwitchBot Plugin UI</ title >
77 < style >
8- body { font-family : system-ui, -apple-system, Arial; padding : 16px ; }
9- h1 { font-size : 18px }
8+ body { font-family : system-ui, -apple-system, Arial; padding : 16px ; background : # 1a1a1a ; color : # fff }
9+ h1 { font-size : 24px ; margin-top : 0 }
10+ h2 { font-size : 16px ; margin-top : 24px ; margin-bottom : 12px ; border-bottom : 1px solid # 444 ; padding-bottom : 8px }
1011 ul { padding-left : 0 ; list-style : none }
1112 li { margin : 8px 0 ; display : flex; gap : 8px ; align-items : center }
12- button { padding : 6px 10px }
13- code { background : # f3f3f3 ; padding : 4px 6px ; border-radius : 4px }
13+ button { padding : 8px 16px ; background : # 6366f1 ; color : # fff ; border : none; border-radius : 4px ; cursor : pointer }
14+ button : hover { background : # 4f46e5 }
15+ button .success { background : # 10b981 }
16+ code { background : # 333 ; padding : 4px 6px ; border-radius : 4px ; color : # fff }
17+ .form-group { margin-bottom : 16px }
18+ label { display : block; margin-bottom : 6px ; font-weight : 500 }
19+ input { width : 100% ; max-width : 400px ; padding : 8px ; background : # 2a2a2a ; border : 1px solid # 444 ; border-radius : 4px ; color : # fff ; font-family : monospace }
20+ input : focus { outline : none; border-color : # 6366f1 }
21+ .status { font-size : 14px ; color : # 888 ; margin-top : 4px }
22+ .status .ok { color : # 10b981 }
23+ .error { color : # ef4444 }
24+ .success-msg { color : # 10b981 ; margin-top : 8px }
25+ .card { background : # 2a2a2a ; padding : 16px ; border-radius : 8px ; margin-bottom : 16px }
1426 </ style >
1527 </ head >
1628 < body >
17- < h1 > SwitchBot: Configured Devices</ h1 >
18- < p > This page lists devices found in your Homebridge config for the SwitchBot platform. Use the copy button to insert device IDs into the plugin configuration. Connection preference (BLE/OpenAPI) is shown when available.</ p >
19- < div id ="status "> Loading…</ div >
20- < ul id ="devices "> </ ul >
29+ < h1 > 🤖 SwitchBot Configuration</ h1 >
30+
31+ < div class ="card ">
32+ < h2 > API Credentials</ h2 >
33+ < p > Configure your SwitchBot API token and secret to enable device discovery and control.</ p >
34+
35+ < div class ="form-group ">
36+ < label for ="token "> API Token:</ label >
37+ < input type ="password " id ="token " placeholder ="Enter your SwitchBot API token " />
38+ < div class ="status " id ="tokenStatus "> </ div >
39+ </ div >
40+
41+ < div class ="form-group ">
42+ < label for ="secret "> API Secret:</ label >
43+ < input type ="password " id ="secret " placeholder ="Enter your SwitchBot API secret " />
44+ < div class ="status " id ="secretStatus "> </ div >
45+ </ div >
46+
47+ < button id ="saveBtn " onclick ="saveCredentials() "> Save Credentials</ button >
48+ < div id ="saveStatus "> </ div >
49+ </ div >
50+
51+ < div class ="card ">
52+ < h2 > Configured Devices</ h2 >
53+ < p > This page lists devices found in your Homebridge config for the SwitchBot platform. Use the copy button to insert device IDs into the plugin configuration. Connection preference (BLE/OpenAPI) is shown when available.</ p >
54+ < div id ="status "> Loading…</ div >
55+ < ul id ="devices "> </ ul >
56+ </ div >
2157
2258 < script >
59+ async function loadCredentialStatus ( ) {
60+ try {
61+ const resp = await homebridge . request ( '/credentials' , { } )
62+ if ( ! resp || ! resp . success === false ) {
63+ if ( ! resp ?. data ) {
64+ console . error ( 'Failed to load credentials:' , resp ?. data ?. message )
65+ return
66+ }
67+ }
68+
69+ const creds = resp . data || { }
70+ const tokenStatus = document . getElementById ( 'tokenStatus' )
71+ const secretStatus = document . getElementById ( 'secretStatus' )
72+
73+ if ( creds . hasToken ) {
74+ tokenStatus . textContent = `✓ Configured (${ creds . tokenLength } characters)`
75+ tokenStatus . classList . add ( 'ok' )
76+ } else {
77+ tokenStatus . textContent = 'Not configured'
78+ }
79+
80+ if ( creds . hasSecret ) {
81+ secretStatus . textContent = `✓ Configured (${ creds . secretLength } characters)`
82+ secretStatus . classList . add ( 'ok' )
83+ } else {
84+ secretStatus . textContent = 'Not configured'
85+ }
86+ } catch ( e ) {
87+ console . error ( 'Error loading credentials:' , e )
88+ }
89+ }
90+
91+ async function saveCredentials ( ) {
92+ const token = document . getElementById ( 'token' ) . value
93+ const secret = document . getElementById ( 'secret' ) . value
94+ const saveStatus = document . getElementById ( 'saveStatus' )
95+ const saveBtn = document . getElementById ( 'saveBtn' )
96+
97+ if ( ! token || ! secret ) {
98+ saveStatus . textContent = 'Please enter both token and secret'
99+ saveStatus . classList . add ( 'error' )
100+ return
101+ }
102+
103+ try {
104+ saveBtn . disabled = true
105+ saveBtn . textContent = 'Saving...'
106+
107+ const resp = await homebridge . request ( '/credentials' , { token, secret } )
108+ if ( ! resp || resp . success === false ) {
109+ throw new Error ( resp ?. data ?. message || 'Save failed' )
110+ }
111+
112+ saveStatus . textContent = '✓ ' + ( resp . data ?. message || 'Credentials saved' )
113+ saveStatus . classList . remove ( 'error' )
114+ saveStatus . classList . add ( 'success-msg' )
115+
116+ // Clear inputs after successful save
117+ document . getElementById ( 'token' ) . value = ''
118+ document . getElementById ( 'secret' ) . value = ''
119+
120+ // Reload status
121+ setTimeout ( ( ) => loadCredentialStatus ( ) , 1000 )
122+
123+ // Clear status message
124+ setTimeout ( ( ) => {
125+ saveStatus . textContent = ''
126+ saveStatus . classList . remove ( 'success-msg' )
127+ } , 3000 )
128+ } catch ( e ) {
129+ saveStatus . textContent = 'Error: ' + ( e ?. message || 'Failed to save' )
130+ saveStatus . classList . add ( 'error' )
131+ } finally {
132+ saveBtn . disabled = false
133+ saveBtn . textContent = 'Save Credentials'
134+ }
135+ }
136+
23137 async function fetchDevices ( ) {
24138 try {
25139 const resp = await homebridge . request ( '/devices' , { } )
@@ -59,7 +173,11 @@ <h1>SwitchBot: Configured Devices</h1>
59173 try {
60174 await navigator . clipboard . writeText ( d . id )
61175 btn . textContent = 'Copied'
62- setTimeout ( ( ) => ( btn . textContent = 'Copy ID' ) , 1200 )
176+ btn . classList . add ( 'success' )
177+ setTimeout ( ( ) => {
178+ btn . textContent = 'Copy ID'
179+ btn . classList . remove ( 'success' )
180+ } , 1200 )
63181 } catch ( e ) {
64182 alert ( 'Failed to copy' )
65183 }
@@ -73,9 +191,10 @@ <h1>SwitchBot: Configured Devices</h1>
73191 }
74192
75193 ( async ( ) => {
194+ await loadCredentialStatus ( )
76195 const list = await fetchDevices ( )
77196 render ( list )
78197 } ) ( )
79198 </ script >
80199 </ body >
81- </ html >
200+ </ html >
0 commit comments