@@ -18,16 +18,18 @@ window.app = Vue.createApp({
1818 lnurlValue : '' ,
1919 qrcodeUrl : '' ,
2020 websocketMessage : '' ,
21- activeWebsocketDeviceId : null ,
2221 activeWebsocket : null ,
22+ activeWebsocketDeviceId : null ,
23+ connectedDevices : [ ] ,
24+ statusPollInterval : null ,
2325 protocol : window . location . protocol ,
2426 wsLocation : '' ,
2527
2628 stats : {
2729 totalDevices : 0 ,
2830 totalSwitches : 0 ,
29- activeSwitches : 0 ,
30- inactiveSwitches : 0
31+ connectedDevices : 0 ,
32+ offlineDevices : 0
3133 } ,
3234
3335 deviceColumns : [
@@ -123,11 +125,9 @@ window.app = Vue.createApp({
123125 } ,
124126 filteredDevices ( ) {
125127 let devices = this . devices
126- // Filter by wallet if not "all"
127128 if ( this . selectedWallet && this . selectedWallet !== 'all' ) {
128129 devices = devices . filter ( d => d . wallet === this . selectedWallet )
129130 }
130- // Filter by search term
131131 if ( this . filter ) {
132132 const search = this . filter . toLowerCase ( )
133133 devices = devices . filter ( d =>
@@ -151,24 +151,53 @@ window.app = Vue.createApp({
151151
152152 methods : {
153153 onWalletChange ( ) {
154- // Filtering is handled by filteredDevices computed property
155154 this . calculateStats ( )
156155 } ,
157156
158157 calculateStats ( ) {
159- // Use filtered devices for stats when wallet is selected
160158 let devices = this . devices
161159 if ( this . selectedWallet && this . selectedWallet !== 'all' ) {
162160 devices = this . devices . filter ( d => d . wallet === this . selectedWallet )
163161 }
164162 this . stats . totalDevices = devices . length
165163 this . stats . totalSwitches = devices . reduce ( ( sum , d ) => sum + ( d . switches ?. length || 0 ) , 0 )
166164
167- // Count active/inactive based on WebSocket connection
168- const activeDeviceId = this . activeWebsocketDeviceId
169- const connectedDevice = activeDeviceId ? devices . find ( d => d . id === activeDeviceId ) : null
170- this . stats . activeSwitches = connectedDevice ? ( connectedDevice . switches ?. length || 0 ) : 0
171- this . stats . inactiveSwitches = this . stats . totalSwitches - this . stats . activeSwitches
165+ // Count connected/offline based on server-side WebSocket tracking
166+ const connectedIds = this . connectedDevices
167+ const filteredIds = devices . map ( d => d . id )
168+ this . stats . connectedDevices = connectedIds . filter ( id => filteredIds . includes ( id ) ) . length
169+ this . stats . offlineDevices = this . stats . totalDevices - this . stats . connectedDevices
170+ } ,
171+
172+ async fetchConnectionStatus ( ) {
173+ try {
174+ const response = await LNbits . api . request (
175+ 'GET' ,
176+ '/devicetimer/api/v1/ws/status' ,
177+ this . g . user . wallets [ 0 ] . inkey
178+ )
179+ if ( response . data && response . data . connected ) {
180+ this . connectedDevices = response . data . connected
181+ this . calculateStats ( )
182+ }
183+ } catch ( err ) {
184+ console . warn ( 'Failed to fetch connection status' )
185+ }
186+ } ,
187+
188+ startStatusPolling ( ) {
189+ // Poll every 60 seconds
190+ this . fetchConnectionStatus ( )
191+ this . statusPollInterval = setInterval ( ( ) => {
192+ this . fetchConnectionStatus ( )
193+ } , 60000 )
194+ } ,
195+
196+ stopStatusPolling ( ) {
197+ if ( this . statusPollInterval ) {
198+ clearInterval ( this . statusPollInterval )
199+ this . statusPollInterval = null
200+ }
172201 } ,
173202
174203 formatHours ( device ) {
@@ -178,7 +207,6 @@ window.app = Vue.createApp({
178207 async getDevices ( ) {
179208 this . loading = true
180209 try {
181- // Always fetch all devices using the first wallet's adminkey
182210 const response = await LNbits . api . request (
183211 'GET' ,
184212 '/devicetimer/api/v1/device' ,
@@ -382,7 +410,8 @@ window.app = Vue.createApp({
382410 return
383411 }
384412
385- const websocketUrl = this . wsLocation + '/api/v1/ws/' + deviceId
413+ // Use extension WebSocket endpoint
414+ const websocketUrl = this . wsLocation + '/devicetimer/api/v1/ws/' + deviceId
386415 this . websocketMessage = 'Connecting...'
387416 this . activeWebsocketDeviceId = deviceId
388417
@@ -391,12 +420,14 @@ window.app = Vue.createApp({
391420 this . activeWebsocket = ws
392421
393422 ws . onopen = ( ) => {
394- this . websocketMessage = 'connected '
395- this . calculateStats ( )
423+ this . websocketMessage = 'Connected '
424+ this . fetchConnectionStatus ( )
396425 }
397426
398- ws . onmessage = ( ) => {
427+ ws . onmessage = ( event ) => {
399428 this . websocketMessage = 'Payment received!'
429+ // Refresh status after payment
430+ setTimeout ( ( ) => this . fetchConnectionStatus ( ) , 1000 )
400431 }
401432
402433 ws . onclose = ( ) => {
@@ -426,7 +457,6 @@ window.app = Vue.createApp({
426457 this . activeWebsocket = null
427458 this . activeWebsocketDeviceId = null
428459 this . websocketMessage = ''
429- this . calculateStats ( )
430460 }
431461 } ,
432462
@@ -442,7 +472,8 @@ window.app = Vue.createApp({
442472 } ,
443473
444474 openWebsocketDialog ( device ) {
445- this . websocketDialog . url = this . wsLocation + '/api/v1/ws/' + device . id
475+ // Show extension WebSocket URL for hardware configuration
476+ this . websocketDialog . url = this . wsLocation + '/devicetimer/api/v1/ws/' + device . id
446477 this . websocketDialog . deviceTitle = device . title
447478 this . websocketDialog . show = true
448479 } ,
@@ -498,8 +529,8 @@ window.app = Vue.createApp({
498529 return amount . toString ( )
499530 } ,
500531
501- isDeviceLive ( deviceId ) {
502- return this . activeWebsocketDeviceId === deviceId && this . activeWebsocket !== null
532+ isDeviceConnected ( deviceId ) {
533+ return this . connectedDevices . includes ( deviceId )
503534 }
504535 } ,
505536
@@ -508,12 +539,18 @@ window.app = Vue.createApp({
508539 this . wsLocation = ( window . location . protocol === 'https:' ? 'wss://' : 'ws://' ) + window . location . host
509540
510541 await this . getDevices ( )
542+ this . startStatusPolling ( )
511543
512544 try {
513545 const response = await LNbits . api . request ( 'GET' , '/devicetimer/api/v1/timezones' )
514546 this . timezones = response . data
515547 } catch ( err ) {
516548 console . warn ( 'Failed to load timezones' )
517549 }
550+ } ,
551+
552+ beforeUnmount ( ) {
553+ this . stopStatusPolling ( )
554+ this . disconnectWebsocket ( )
518555 }
519556} )
0 commit comments