import { createSocket , Socket } from "dgram" ; import { EventEmitter } from "events" ; import { address , toBuffer } from "ip" ; import { AddressInfo } from "net" ; import { checkPortStatus } from "portscanner" ; import { defaultLogger } from "./logger" ; import { IDevice } from "./models/device" ; import { IDiscoverConfig } from "./models/discover-config" ; import { ILogger } from "./models/logger" ; import { Utils } from "./utils" ; constructor param { string } title param { string } author export class Discover extends EventEmitter { private devices : IDevice [ ] ; private timer : any ; private options : IDiscoverConfig = { debug : true , fallback : true , host : null , limit : 1 , multicastHost : "239.255.255.250" , port : 1982 , timeout : 10000 } ; private client : Socket ; private clientBound = false ; private isDestroyed = false ; constructor param { IDiscoverConfig } options discover object include the port and multicast host.
     * see  IDiscoverConfig for more detail
     *  param { ILogger } logger constructor ( options , private logger ? ) { super ( ) ; this . devices = [ ] ; this . options = { ... this . options , ... options } ; this . client = createSocket ( "udp4" ) ; this . client . on ( "message" , this . onSocketMessage . bind ( this ) ) ; this . client . on ( "error" , this . onError . bind ( this ) ) ; this . clientBound = false ; this . logger = logger || defaultLogger ; } param ipAddress public async detectLightIP ( ipAddress ) { const device = { host : ipAddress , port : 55443 } ; return new Promise < IDevice > ( ( resolve , reject ) => { checkPortStatus ( 55443 , ipAddress , ( err , status ) => { if ( err || status === "closed" ) { return resolve ( null ) ; } else { this . addDevice ( device as IDevice ) ; return resolve ( device as IDevice ) ; } } ) ; } ) ; } requires public async scanByIp ( ) { const localIp = address ( ) ; const count = 0 ; const availabledIps = Utils . getListIpAddress ( localIp ) ; const promises = availabledIps . map ( ( x ) => this . detectLightIP ( x ) ) ; await Promise . all ( promises ) ; return this . devices ; } returns { Promise < IDevice [ ] > } public start ( ) { return new Promise ( ( resolve , reject ) => { try { if ( ! this . clientBound ) { this . clientBound = true ; this . client . bind ( this . options . port , null , resolve as any ) ; } else { resolve ( null ) ; } } catch ( e ) { reject ( e ) ; } } ) . then ( ( ) => { return new Promise < IDevice [ ] > ( ( resolve , reject ) => { this . logger . debug ( "discover options: " , this . options ) ; this . client . setBroadcast ( true ) ; this . client . send ( this . getMessage ( ) , this . options . port , this . options . multicastHost , ( err ) => { if ( err ) { this . logger . log ( "ERROR" , err ) ; reject ( err ) ; } else { let ts = 0 ; const interval = this . options . scanInterval || 200 ; let timer = null ; const callback = ( error , result ? ) => { if ( timer ) { clearInterval ( timer ) ; timer = null ; } if ( error ) { reject ( error ) ; } else { resolve ( result ) ; } } ; timer = setInterval ( ( ) => { if ( this . isDestroyed ) { callback ( "Discover got destroyed" ) ; return ; } ts += interval ; if ( this . options . limit && this . devices . length >= this . options . limit ) { clearInterval ( this . timer ) ; callback ( null , this . devices ) ; return ; } if ( this . options . timeout > 0 && this . options . timeout < ts ) { if ( this . devices . length > 0 ) { clearInterval ( this . timer ) ; callback ( null , this . devices ) ; return ; } else { clearInterval ( this . timer ) ; if ( ! this . options . fallback ) { callback ( "No device found after timeout exceeded : " + ts ) ; return ; } } } this . client . send ( this . getMessage ( ) , this . options . port , this . options . multicastHost ) ; if ( ts > this . options . timeout && this . options . fallback ) { this . scanByIp ( ) . catch ( ( error ) => { callback ( error ) ; } ) ; } } , interval ) ; } } ) ; } ) ; } ) ; } returns { Promise } public destroy ( ) { this . isDestroyed = true ; if ( ! this . client ) { return Promise . resolve ( ) ; } return new Promise ( ( resolve ) => { this . removeAllListeners ( ) ; if ( this . client ) { this . client . close ( resolve ) ; } } ) ; } param error private onError ( error ) { this . logger . error ( "Internal Error " , error ) ; this . emit ( "error" , error ) ; } /**
     * Generate the UDP message to discover device on local network.
     */ private getMessage ( ) { const message = `M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1982\r\nMAN: "ssdp:discover"\r\nST: wifi_bulb\r\n` ; return Buffer . from ( message ) ; } param { Buffer } buffer param { AddressInfo } address private onSocketMessage ( buffer , addressInfo ) { const message = buffer . toString ( ) ; if ( this . options . debug && this . logger ) { this . logger . info ( message ) ; } const device = Utils . parseDeviceInfo ( message ) ; if ( device ) { this . addDevice ( device ) ; } } param { IDevice } device returns { 0 | 1 } private addDevice ( device ) { if ( ! this . options . filter || this . options . filter ( device ) ) { const existDevice = this . devices . findIndex ( ( x ) => { return ( x . host && device . host && x . host === device . host && x . port && device . port && x . port === device . port ) ; } ) ; if ( existDevice === - 1 ) { this . devices . push ( device ) ; this . emit ( "deviceAdded" , device ) ; } this . devices [ existDevice ] = device ; } } }