import type { ZWaveLogContainer } from "@zwave-js/core" ; import { Mixin } from "@zwave-js/shared" ; import { EventEmitter } from "events" ; import { Duplex , PassThrough , Readable , Writable } from "stream" ; import { SerialLogger } from "./Logger" ; import { MessageHeaders } from "./MessageHeaders" ; import { SerialAPIParser } from "./SerialAPIParser" ; export type ZWaveSerialChunk = MessageHeaders . ACK | MessageHeaders . NAK | MessageHeaders . CAN | Buffer ; export interface ZWaveSerialPortEventCallbacks { error : ( e : Error ) => void ; data : ( data : ZWaveSerialChunk ) => void ; } export type ZWaveSerialPortEvents = Extract < keyof ZWaveSerialPortEventCallbacks , string > ; export interface ZWaveSerialPortBase { on < TEvent extends ZWaveSerialPortEvents > ( event , callback ) : this ; addListener < TEvent extends ZWaveSerialPortEvents > ( event , callback ) : this ; once < TEvent extends ZWaveSerialPortEvents > ( event , callback ) : this ; off < TEvent extends ZWaveSerialPortEvents > ( event , callback ) : this ; removeListener < TEvent extends ZWaveSerialPortEvents > ( event , callback ) : this ; removeAllListeners ( event ? ) : this ; emit < TEvent extends ZWaveSerialPortEvents > ( event , ... args ) : boolean ; } export interface ZWaveSerialPortImplementation { create ( ) : Duplex & EventEmitter ; open ( port ) : Promise < void > ; close ( port ) : Promise < void > ; } @ Mixin ( [ EventEmitter ] ) export class ZWaveSerialPortBase extends PassThrough { protected serial : ReturnType < ZWaveSerialPortImplementation [ "create" ] > ; private parser : SerialAPIParser ; protected logger : SerialLogger ; public declare [ Symbol . asyncIterator ] : ( ) => AsyncIterableIterator < ZWaveSerialChunk > ; constructor ( private implementation , loggers ) { super ( { readableObjectMode : true } ) ; for ( const method of [ "on" , "once" , "off" , "addListener" , "removeListener" , "removeAllListeners" , ] as const ) { const original = this [ method ] . bind ( this ) ; this [ method ] = ( event , ... args ) => { if ( event === "data" ) { this . parser [ method ] ( "data" , ... args ) ; } else { ( original as any ) ( event , ... args ) ; } return this ; } ; } this . logger = new SerialLogger ( loggers ) ; this . serial = implementation . create ( ) . on ( "error" , ( e ) => { this . emit ( "error" , e ) ; } ) ; this . parser = new SerialAPIParser ( this . logger ) ; this . serial . pipe ( this . parser ) ; this . pipe = this . parser . pipe . bind ( this . parser ) ; this . unpipe = ( destination ) => { this . parser . unpipe ( destination ) ; return this ; } ; this . on ( "pipe" as any , ( source ) => { source . unpipe ( this as any ) ; source . pipe ( this . serial as unknown as Writable , { end : false } ) ; } ) ; this [ Symbol . asyncIterator ] = ( ) => this . parser [ Symbol . asyncIterator ] ( ) ; } public open ( ) { return this . implementation . open ( this . serial ) . then ( ( ) => { this . _isOpen = true ; } ) ; } public close ( ) { this . _isOpen = false ; return this . implementation . close ( this . serial ) ; } private _isOpen : boolean = false ; public get isOpen ( ) : boolean { return this . _isOpen ; } public async writeAsync ( data ) { if ( ! this . isOpen ) { throw new Error ( "The serial port is not open!" ) ; } if ( data . length === 1 ) { switch ( data [ 0 ] ) { case MessageHeaders . ACK : this . logger . ACK ( "outbound" ) ; break ; case MessageHeaders . CAN : this . logger . CAN ( "outbound" ) ; break ; case MessageHeaders . NAK : this . logger . NAK ( "outbound" ) ; break ; } } else { this . logger . data ( "outbound" , data ) ; } return new Promise ( ( resolve , reject ) => { this . serial . write ( data , ( err ) => { if ( err ) reject ( err ) ; else resolve ( ) ; } ) ; } ) ; } }