"use strict" ; import { readFileSync } from "fs" ; import { PeerCertificate , checkServerIdentity as checkServerIdentityOriginal , connect , TLSSocket } from "tls" ; import { KLF200SocketProtocol } from "./KLF200-API/KLF200SocketProtocol" ; import { IGW_FRAME_RCV , IGW_FRAME_REQ , GatewayCommand , GW_COMMON_STATUS , KLF200_PORT , GW_FRAME_COMMAND_REQ , IGW_FRAME_COMMAND } from "./KLF200-API/common" ; import { join } from "path" ; import { GW_ERROR_NTF } from "./KLF200-API/GW_ERROR_NTF" ; import { GW_PASSWORD_ENTER_CFM , GW_PASSWORD_ENTER_REQ } from "." ; import { Disposable , Listener , TypedEvent } from "./utils/TypedEvent" ; import { timeout as promiseTimeout } from "promise-timeout" ; import { GW_GET_STATE_REQ } from "./KLF200-API/GW_GET_STATE_REQ" ; export interface export interface IConnection { param { string } password param { number } timeout returns { Promise < void > } memberof loginAsync ( password , timeout ? ) : Promise < void > ; param { number } timeout returns { Promise < void > } memberof logoutAsync ( timeout ? ) : Promise < void > ; param { IGW_FRAME_REQ } frame param { number } timeout returns { Promise < IGW_FRAME_RCV > } memberof sendFrameAsync ( frame , timeout ? ) : Promise < IGW_FRAME_RCV > ; param { Listener < IGW_FRAME_RCV > } handler param { GatewayCommand [ ] } filter returns { Disposable } memberof on ( handler , filter ? ) : Disposable ; param { Listener < IGW_FRAME_REQ > } handler param { GatewayCommand [ ] } filter returns { Disposable } memberof onFrameSent ( handler , filter ? ) : Disposable ; type { KLF200SocketProtocol } memberof readonly KLF200SocketProtocol ? : KLF200SocketProtocol ; } const FINGERPRINT = "02:8C:23:A0:89:2B:62:98:C4:99:00:5B:D2:E7:2E:0A:70:3D:71:6A" ; const ca = readFileSync ( join ( __dirname , "../velux-cert.pem" ) ) ; export class export class Connection implements IConnection { private sckt ? : TLSSocket ; private klfProtocol ? : KLF200SocketProtocol ; param { string } host param { Buffer } CA param { string } fingerprint memberof constructor ( readonly host , readonly CA = ca , readonly fingerprint = FINGERPRINT ) { } readonly memberof public get KLF200SocketProtocol ( ) : KLF200SocketProtocol | undefined { return this . klfProtocol ; } private param { string } password returns { Promise < void > } memberof private async _loginAsync ( password ) { try { await this . initSocketAsync ( ) ; this . klfProtocol = new KLF200SocketProtocol ( < TLSSocket > this . sckt ) ; const passwordCFM = < GW_PASSWORD_ENTER_CFM > await this . sendFrameAsync ( new GW_PASSWORD_ENTER_REQ ( password ) ) ; if ( passwordCFM . Status !== GW_COMMON_STATUS . SUCCESS ) { return Promise . reject ( new Error ( "Login failed." ) ) ; } else { return Promise . resolve ( ) ; } } catch ( error ) { return Promise . reject ( error ) ; } } param { string } password param { number } timeout returns { Promise < void > } memberof public async loginAsync ( password , timeout = 60 ) { try { return promiseTimeout ( this . _loginAsync ( password ) , timeout * 1000 ) ; } catch ( error ) { return Promise . reject ( error ) ; } } param { number } timeout returns { Promise < void > } memberof public async logoutAsync ( timeout = 10 ) { try { if ( this . sckt ) { if ( this . klfProtocol ) { this . klfProtocol = undefined ; } return promiseTimeout ( new Promise < void > ( ( resolve , reject ) => { try { this . sckt ?. end ( "" , resolve ) ; } catch ( error ) { reject ( error ) ; } } ) , timeout * 1000 ) ; } else { return Promise . resolve ( ) ; } } catch ( error ) { Promise . reject ( error ) ; } } param { IGW_FRAME_REQ } frame param { number } timeout returns { Promise < IGW_FRAME_RCV > } memberof public async sendFrameAsync ( frame , timeout = 10 ) { try { const frameName = GatewayCommand [ frame . Command ] ; const expectedConfirmationFrameName = frameName . slice ( 0 , - 3 ) + "CFM" as keyof typeof GatewayCommand ; const expectedConfirmationFrameCommand = GatewayCommand [ expectedConfirmationFrameName ] ; const sessionID = frame instanceof GW_FRAME_COMMAND_REQ ? frame . SessionID : undefined ; return promiseTimeout ( new Promise < IGW_FRAME_RCV > ( ( resolve , reject ) => { try { const cfmHandler = ( this . klfProtocol as KLF200SocketProtocol ) . on ( ( frame ) => { try { if ( frame instanceof GW_ERROR_NTF ) { cfmHandler . dispose ( ) ; reject ( new Error ( frame . getError ( ) ) ) ; } else if ( frame . Command === expectedConfirmationFrameCommand && ( typeof sessionID === "undefined" || sessionID === ( frame as IGW_FRAME_COMMAND ) . SessionID ) ) { cfmHandler . dispose ( ) ; resolve ( frame ) ; } } catch ( error ) { reject ( error ) ; } } ) ; this . shiftKeepAlive ( ) ; ( this . klfProtocol as KLF200SocketProtocol ) . write ( frame . Data ) ; this . notifyFrameSent ( frame ) ; } catch ( error ) { reject ( error ) ; } } ) , timeout * 1000 ) ; } catch ( error ) { return Promise . reject ( error ) ; } } param { Listener < IGW_FRAME_RCV > } handler param { GatewayCommand [ ] } filter returns { Disposable } memberof public on ( handler , filter ? ) { if ( typeof filter === "undefined" ) { return ( this . klfProtocol as KLF200SocketProtocol ) . on ( handler ) ; } else { return ( this . klfProtocol as KLF200SocketProtocol ) . on ( ( frame ) => { if ( filter . indexOf ( frame . Command ) >= 0 ) { handler ( frame ) ; } } ) ; } } private _onFrameSent = new TypedEvent < IGW_FRAME_REQ > ( ) ; param { Listener < IGW_FRAME_REQ > } handler param { GatewayCommand [ ] } filter returns { Disposable } memberof public onFrameSent ( handler , filter ? ) { if ( typeof filter === "undefined" ) { return this . _onFrameSent . on ( handler ) ; } else { return this . _onFrameSent . on ( ( frame ) => { if ( filter . indexOf ( frame . Command ) >= 0 ) { handler ( frame ) ; } } ) ; } } private notifyFrameSent ( frame ) { this . _onFrameSent . emit ( frame ) ; } private keepAliveTimer ? : NodeJS . Timeout ; private keepAliveInterval : number = 10 * 60 * 1000 ; param { number } interval memberof public startKeepAlive ( interval = 10 * 60 * 1000 ) { this . keepAliveInterval = interval ; this . keepAliveTimer = setInterval ( ( ) => { this . sendKeepAlive ( ) ; } , interval ) ; } memberof public stopKeepAlive ( ) { if ( this . keepAliveTimer ) { clearInterval ( this . keepAliveTimer ) ; this . keepAliveTimer = undefined ; } } private returns { Promise < void > } memberof private async sendKeepAlive ( ) { await this . sendFrameAsync ( new GW_GET_STATE_REQ ( ) ) ; return ; } private memberof private shiftKeepAlive ( ) { if ( this . keepAliveTimer ) { clearInterval ( this . keepAliveTimer ) ; this . startKeepAlive ( this . keepAliveInterval ) ; } } private async initSocketAsync ( ) { try { if ( this . sckt === undefined ) { return new Promise < void > ( ( resolve , reject ) => { try { const loginErrorHandler = ( error ) => { this . sckt = undefined ; reject ( error ) ; } ; this . sckt = connect ( KLF200_PORT , this . host , { rejectUnauthorized : true , ca : [ this . CA ] , checkServerIdentity : ( host , cert ) => this . checkServerIdentity ( host , cert ) } , ( ) => { if ( this . sckt ?. authorized ) { this . sckt ?. off ( "error" , loginErrorHandler ) ; resolve ( ) ; } else { const err = this . sckt ?. authorizationError ; this . sckt = undefined ; reject ( err ) ; } } ) ; this . sckt ?. on ( "error" , loginErrorHandler ) ; this . sckt ?. once ( "close" , ( ) => { this . stopKeepAlive ( ) ; this . klfProtocol = undefined ; this . sckt = undefined ; } ) ; } catch ( error ) { reject ( error ) ; } } ) ; } else { return Promise . resolve ( ) ; } } catch ( error ) { return Promise . reject ( error ) ; } } private checkServerIdentity ( host , cert ) { if ( cert . fingerprint === this . fingerprint ) return undefined ; else return checkServerIdentityOriginal ( host , cert ) ; } }