@@ -374,58 +374,83 @@ class BituoPanel extends HTMLElement {
374374 }
375375 }
376376
377- showConfirmationDialog ( message , onConfirm ) {
378- const dialog = this . querySelector ( '#confirmation-dialog' ) ;
379- const messageElement = this . querySelector ( '#confirmation-message' ) ;
380- const yesButton = this . querySelector ( '#confirm-yes' ) ;
381- const noButton = this . querySelector ( '#confirm-no' ) ;
382-
383- messageElement . textContent = message ;
384- dialog . style . display = 'block' ;
385-
386- yesButton . onclick = ( ) => {
387- dialog . style . display = 'none' ;
388- onConfirm ( ) ;
389- } ;
390-
391- noButton . onclick = ( ) => {
392- dialog . style . display = 'none' ;
393- this . hideOtaOverlay ( ) ;
394- } ;
395- }
396-
397377 async setDataRequestFrequency ( ) {
398378 const frequency = parseInt ( this . querySelector ( '#data-frequency' ) . value ) ;
399-
379+
400380 if ( isNaN ( frequency ) || frequency < 5 || frequency > 3600 ) {
401381 this . showAlert ( 'Polling Interval must be between 5 and 3600 seconds.' ) ;
402382 this . hideOtaOverlay ( ) ;
403383 return ;
404384 }
405385 const applyToAll = this . querySelector ( '#apply-to-all' ) . checked ;
406-
386+
387+ const offlineDevices = [ ] ;
388+ const onlineTasks = [ ] ;
389+ const deviceSelectElement = this . querySelector ( '#device-select' ) ;
390+ const options = Array . from ( deviceSelectElement . options ) . filter ( option => option . value ) ;
391+
407392 if ( applyToAll ) {
408- const deviceSelectElement = this . querySelector ( '#device-select' ) ;
409- const options = Array . from ( deviceSelectElement . options ) . filter ( option => option . value ) ;
410-
411- // 逐个发送请求,确保每个设备的设置被正确更新
393+ // 收集任务并检查设备在线状态
412394 for ( const option of options ) {
413395 const deviceIp = option . value ;
414- try {
415- await this . updateDeviceFrequency ( deviceIp , frequency ) ;
416- } catch ( error ) {
417- this . showAlert ( `Error updating frequency for device ${ deviceIp } : ${ error . message } ` ) ;
418- }
396+ const task = async ( ) => {
397+ try {
398+ const isOnline = await this . checkDeviceStatus ( deviceIp ) ;
399+ if ( isOnline ) {
400+ await this . updateDeviceFrequency ( deviceIp , frequency ) ;
401+ } else {
402+ offlineDevices . push ( deviceIp ) ;
403+ }
404+ } catch ( error ) {
405+ offlineDevices . push ( deviceIp ) ;
406+ }
407+ } ;
408+ onlineTasks . push ( task ) ;
419409 }
420410 } else {
421411 const { deviceIp } = this . getSelectedDevice ( ) ;
422412 if ( ! deviceIp ) {
423413 this . showAlert ( 'No device selected.' ) ;
424414 return ;
425415 }
426- await this . updateDeviceFrequency ( deviceIp , frequency ) ;
416+ const task = async ( ) => {
417+ try {
418+ const isOnline = await this . checkDeviceStatus ( deviceIp ) ;
419+ if ( isOnline ) {
420+ await this . updateDeviceFrequency ( deviceIp , frequency ) ;
421+ } else {
422+ offlineDevices . push ( deviceIp ) ;
423+ }
424+ } catch ( error ) {
425+ offlineDevices . push ( deviceIp ) ;
426+ }
427+ } ;
428+ onlineTasks . push ( task ) ;
429+ }
430+
431+ // 限制并发数量
432+ await this . runWithConcurrencyLimit ( onlineTasks , 10 ) ;
433+
434+ const totalDevices = options . length ;
435+ const offlineCount = offlineDevices . length ;
436+ this . showAlert ( `Total devices: ${ totalDevices } . Offline devices: ${ offlineCount } . Polling interval has been successfully updated for all online devices.` ) ;
437+ }
438+
439+ async runWithConcurrencyLimit ( tasks , limit ) {
440+ const results = [ ] ;
441+ const executing = [ ] ;
442+ for ( const task of tasks ) {
443+ const p = task ( ) . then ( result => {
444+ executing . splice ( executing . indexOf ( p ) , 1 ) ;
445+ return result ;
446+ } ) ;
447+ results . push ( p ) ;
448+ executing . push ( p ) ;
449+ if ( executing . length >= limit ) {
450+ await Promise . race ( executing ) ;
451+ }
427452 }
428- this . showAlert ( `Polling Interval set successfully.` ) ;
453+ return Promise . all ( results ) ;
429454 }
430455
431456 async updateDeviceFrequency ( deviceIp , frequency ) {
@@ -456,21 +481,51 @@ class BituoPanel extends HTMLElement {
456481 clearInterval ( this . intervalId ) ;
457482 }
458483
484+ async limitConcurrency ( tasks , limit ) {
485+ const results = [ ] ;
486+ const executing = [ ] ;
487+
488+ for ( const task of tasks ) {
489+ const p = task ( ) ; // 执行任务
490+ results . push ( p ) ;
491+
492+ if ( limit <= tasks . length ) {
493+ const e = p . then ( ( ) => executing . splice ( executing . indexOf ( e ) , 1 ) ) ;
494+ executing . push ( e ) ;
495+ if ( executing . length >= limit ) {
496+ await Promise . race ( executing ) ;
497+ }
498+ }
499+ }
500+ return Promise . all ( results ) ;
501+ }
502+
459503 async loadDevices ( ) {
460504 const deviceSelectElement = this . querySelector ( '#device-select' ) ;
461505 const selectedDeviceIp = deviceSelectElement . value ; // 保存当前选中的设备IP
462506 deviceSelectElement . innerHTML = '<option value="" disabled selected>Loading devices...</option>' ;
507+
463508 try {
464509 const response = await this . _hass . callApi ( 'GET' , 'bituopmd/devices' ) ;
465510 deviceSelectElement . innerHTML = '' ; // 清空之前的内容
511+
466512 if ( response && response . length ) {
467- for ( const device of response ) {
513+ const tasks = response . map ( device => async ( ) => {
468514 const option = this . findOrCreateOption ( deviceSelectElement , device . ip ) ;
469- const isOnline = await this . checkDeviceStatus ( device . ip ) ;
515+ let isOnline = false ;
516+ try {
517+ isOnline = await this . checkDeviceStatus ( device . ip ) ;
518+ } catch ( error ) {
519+ isOnline = false ; // 超时或错误,视为离线
520+ }
470521 option . text = isOnline ? device . name : `${ device . name } (offline)` ;
471522 option . disabled = ! isOnline ;
472523 deviceSelectElement . add ( option ) ;
473- }
524+ } ) ;
525+
526+ // 使用并发限制执行任务,每次最多执行10个
527+ await this . limitConcurrency ( tasks , 10 ) ;
528+
474529 // 恢复之前选中的设备
475530 deviceSelectElement . value = selectedDeviceIp ;
476531 } else {
@@ -491,12 +546,33 @@ class BituoPanel extends HTMLElement {
491546 return option ;
492547 }
493548
494- async checkDeviceStatus ( ip ) {
549+ async checkDeviceStatus ( ip , timeout = 3000 ) {
550+ const url = `bituopmd/proxy/${ ip } /data` ;
551+
552+ // 创建超时的 Promise
553+ const timeoutPromise = new Promise ( ( _ , reject ) =>
554+ setTimeout ( ( ) => reject ( new Error ( 'Request timed out' ) ) , timeout )
555+ ) ;
556+
557+ // 创建 API 请求的 Promise
558+ const apiPromise = this . _hass . callApi ( 'GET' , url )
559+ . then ( response => {
560+ // 确保有有效的响应数据
561+ if ( response && response . response ) {
562+ // 根据实际数据来判断设备状态
563+ return response . response !== "404: Not Found" ;
564+ } else {
565+ // 没有有效数据时视为离线
566+ return false ;
567+ }
568+ } )
569+ . catch ( ( ) => false ) ; // 处理请求错误
570+
495571 try {
496- const response = await this . _hass . callApi ( 'GET' , `bituopmd/proxy/ ${ ip } /data` ) ;
497- return response . response && response . response !== "404: Not Found" ;
572+ // 使用 Promise.race 来处理超时和 API 请求
573+ return await Promise . race ( [ apiPromise , timeoutPromise ] ) ;
498574 } catch {
499- return false ;
575+ return false ; // 返回 false 表示设备离线或请求超时
500576 }
501577 }
502578
0 commit comments