@@ -12,6 +12,7 @@ import type {
1212import type { Index , OpenDTUConfig } from '@/types/settings' ;
1313
1414import { rootLogger } from '@/utils/log' ;
15+ import { downloadFirmware , uploadFirmware } from '@/firmware' ;
1516
1617const log = rootLogger . extend ( 'OpenDtuApi' ) ;
1718
@@ -75,7 +76,7 @@ class OpenDtuApi {
7576 constructor ( ) {
7677 this . wsId = Math . random ( ) . toString ( 36 ) . substring ( 2 , 9 ) ;
7778
78- log . info ( 'OpenDtuApi.constructor()' ) ;
79+ log . debug ( 'OpenDtuApi.constructor()' ) ;
7980 }
8081
8182 public setLocale ( locale : string ) : void {
@@ -95,7 +96,7 @@ class OpenDtuApi {
9596 }
9697
9798 public startFetchHttpStateInterval ( ) : void {
98- log . info ( 'OpenDtuApi.startFetchHttpStateInterval()' ) ;
99+ log . debug ( 'OpenDtuApi.startFetchHttpStateInterval()' ) ;
99100
100101 if ( this . fetchHttpStateInterval ) {
101102 clearInterval ( this . fetchHttpStateInterval ) ;
@@ -104,7 +105,7 @@ class OpenDtuApi {
104105 this . fetchHttpStateInterval = setInterval ( ( ) => {
105106 this . updateHttpState ( ) ;
106107
107- log . info (
108+ log . debug (
108109 'interval -> OpenDtuApi.updateHttpState()' ,
109110 new Date ( ) ,
110111 this . index ,
@@ -181,8 +182,10 @@ class OpenDtuApi {
181182 controller . abort ( ) ;
182183 } , 5000 ) ;
183184
184- log . info ( 'getSystemStatusFromUrl' , url ) ;
185- const response = await fetch ( `${ url . origin } /api/system/status` , {
185+ log . debug ( 'getSystemStatusFromUrl' , url ) ;
186+ const path = `${ url . origin } /api/system/status` ;
187+
188+ const response = await fetch ( path , {
186189 signal : controller . signal ,
187190 method : 'GET' ,
188191 headers : {
@@ -296,7 +299,7 @@ class OpenDtuApi {
296299 } ;
297300
298301 try {
299- log . info ( 'checkCredentials' , baseUrl , requestOptions ) ;
302+ log . debug ( 'checkCredentials' , baseUrl , requestOptions ) ;
300303 const response = await fetch (
301304 `${ baseUrl } /api/security/authenticate` ,
302305 requestOptions ,
@@ -317,7 +320,7 @@ class OpenDtuApi {
317320 }
318321 }
319322
320- public connect ( noInterval = false ) : void {
323+ public connect ( ) : void {
321324 // connect websocket
322325 if ( this . ws !== null ) {
323326 log . warn ( 'OpenDtuApi.connect() ws not null, aborting!' ) ;
@@ -332,17 +335,15 @@ class OpenDtuApi {
332335
333336 const url = `${ protocol } ://${ authString ?? '' } ${ host } /livedata` ;
334337
335- log . info ( 'OpenDtuApi.connect()' , url ) ;
338+ log . debug ( 'OpenDtuApi.connect()' , url ) ;
336339
337340 this . ws = new WebSocket ( url ) ;
338341
339342 this . ws . onopen = ( ) => {
340343 this . wsUrl = url ;
341- log . info ( 'OpenDtuApi.onopen()' ) ;
344+ log . debug ( 'OpenDtuApi.onopen()' ) ;
342345
343- if ( ! noInterval ) {
344- this . startFetchHttpStateInterval ( ) ;
345- }
346+ this . startFetchHttpStateInterval ( ) ;
346347
347348 this . wsConnected = true ;
348349
@@ -352,7 +353,7 @@ class OpenDtuApi {
352353 } ;
353354
354355 this . ws . onmessage = evt => {
355- log . info ( 'OpenDtuApi.onmessage()' ) ;
356+ log . debug ( 'OpenDtuApi.onmessage()' ) ;
356357
357358 let parsedData : LiveDataFromWebsocket | null = null ;
358359
@@ -406,20 +407,20 @@ class OpenDtuApi {
406407 }
407408
408409 setTimeout ( ( ) => {
409- log . debug ( 'Reconnecting websocket' , {
410+ log . info ( 'Reconnecting websocket' , {
410411 wsHost : host ,
411412 currentUrl : this . baseUrl ,
412413 wsId : this . wsId ,
413414 } ) ;
414- this . connect ( true ) ;
415+ this . connect ( ) ;
415416 } , 1000 ) ;
416417 } ;
417418
418419 this . ws . onerror = evt => {
419420 log . error ( 'OpenDtuApi.onerror()' , evt . message ) ;
420421 } ;
421422
422- log . info ( 'OpenDtuApi.connect()' , url ) ;
423+ log . debug ( 'OpenDtuApi.connect()' , url ) ;
423424 }
424425 }
425426
@@ -472,15 +473,15 @@ class OpenDtuApi {
472473 }
473474
474475 public setConfig ( config : OpenDTUConfig , index : Index ) : void {
475- log . info ( 'OpenDtuApi.setConfig()' , { config, index } ) ;
476+ log . debug ( 'OpenDtuApi.setConfig()' , { config, index } ) ;
476477
477478 this . setBaseUrl ( config . baseUrl ) ;
478479 this . setUserString ( config . userString ) ;
479480 this . setIndex ( index ) ;
480481 }
481482
482483 public async updateHttpState ( ) : Promise < void > {
483- log . info ( 'OpenDtuApi.updateHttpState()' ) ;
484+ log . debug ( 'OpenDtuApi.updateHttpState()' ) ;
484485
485486 if ( this . index === null ) {
486487 log . warn ( 'OpenDtuApi.updateHttpState() index is null' ) ;
@@ -704,6 +705,107 @@ class OpenDtuApi {
704705 wsReadyState : this . ws ?. readyState ?? 'undefined' ,
705706 } ;
706707 }
708+
709+ public async downloadOTA (
710+ version : string ,
711+ downloadUrl : string ,
712+ onDownloadProgressEvent : ( progress : number ) => void ,
713+ ) : Promise < string | null > {
714+ return await downloadFirmware (
715+ version ,
716+ downloadUrl ,
717+ onDownloadProgressEvent ,
718+ ) ;
719+ }
720+
721+ public async handleOTA (
722+ version : string ,
723+ path : string | null ,
724+ onUploadProgressEvent : ( progress : number ) => void ,
725+ ) : Promise < boolean > {
726+ if ( ! path ) {
727+ log . error ( 'handleOTA' , 'download failed' ) ;
728+ return false ;
729+ }
730+
731+ const headers = {
732+ ...( this . userString ? { Authorization : 'Basic ' + this . userString } : { } ) ,
733+ } ;
734+
735+ const authString = this . getAuthString ( ) ;
736+
737+ const url = `${ authString ?? '' } ${ this . baseUrl } /api/firmware/update` ;
738+
739+ const res = await uploadFirmware (
740+ version ,
741+ path ,
742+ url ,
743+ headers ,
744+ onUploadProgressEvent ,
745+ ) ;
746+
747+ return res ?. statusCode === 200 ;
748+ }
749+
750+ public awaitForUpdateFinish ( ) : Promise < void > {
751+ return new Promise ( ( resolve , reject ) => {
752+ // fetch from /api/system/status using HTTP HEAD. if okay, resolve. after 1 minute, reject.
753+ let fetchInterval : NodeJS . Timeout | null = null ;
754+
755+ const rejectTimeout = setTimeout ( ( ) => {
756+ log . warn ( 'waiting took too long' ) ;
757+
758+ if ( fetchInterval ) {
759+ clearInterval ( fetchInterval ) ;
760+ }
761+
762+ reject ( ) ;
763+ } , 60 * 1000 ) ;
764+
765+ const authString = this . getAuthString ( ) ;
766+
767+ const url = `${ authString ?? '' } ${ this . baseUrl } /api/system/status` ;
768+
769+ const execFetch = ( ) => {
770+ const controller = new AbortController ( ) ;
771+
772+ const requestOptions = {
773+ method : 'HEAD' ,
774+ signal : controller . signal ,
775+ headers : {
776+ 'X-Requested-With' : 'XMLHttpRequest' ,
777+ 'Content-Type' : 'application/json' ,
778+ ...( this . userString
779+ ? { Authorization : 'Basic ' + this . userString }
780+ : { } ) ,
781+ } ,
782+ } ;
783+
784+ const abortTimeout = setTimeout ( ( ) => {
785+ controller . abort ( ) ;
786+ } , 1000 * 3 ) ;
787+
788+ fetch ( url , requestOptions )
789+ . then ( response => {
790+ if ( response . status === 200 ) {
791+ clearTimeout ( abortTimeout ) ;
792+ clearTimeout ( rejectTimeout ) ;
793+
794+ if ( fetchInterval ) {
795+ clearInterval ( fetchInterval ) ;
796+ }
797+
798+ resolve ( ) ;
799+ }
800+ } )
801+ . catch ( ( ) => { } ) ;
802+ } ;
803+
804+ fetchInterval = setInterval ( ( ) => {
805+ execFetch ( ) ;
806+ } , 3000 ) ;
807+ } ) ;
808+ }
707809}
708810
709811export type DebugInfo = ReturnType < OpenDtuApi [ 'getDebugInfo' ] > ;
0 commit comments