knxultimate
Advanced tools
Comparing version 2.1.2 to 2.1.3
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { RemoteInfo } from 'dgram'; | ||
import { ConnectionStatus } from './protocol/KNXConstants'; | ||
import { KnxResponse } from './protocol/KNXProtocol'; | ||
import { TunnelTypes } from './protocol/TunnelCRI'; | ||
import KNXAddress from './protocol/KNXAddress'; | ||
import KNXDataBuffer from './protocol/KNXDataBuffer'; | ||
import { KNXLoggerOptions } from './KnxLog'; | ||
@@ -67,14 +71,6 @@ import { KNXPacket } from './protocol'; | ||
export declare function getDecodedKeyring(): string; | ||
export declare enum KNXTimer { | ||
ACK = "ack", | ||
HEARTBEAT = "heartbeat", | ||
CONNECTION_STATE = "connection_state", | ||
CONNECTION = "connection", | ||
CONNECT_REQUEST = "connect_request", | ||
DISCONNECT = "disconnect", | ||
DISCOVERY = "discovery" | ||
} | ||
export default class KNXClient extends TypedEventEmitter<KNXClientEventCallbacks> { | ||
private _channelID; | ||
private _connectionState; | ||
private _timerWaitingForACK; | ||
private _numFailedTelegramACK; | ||
@@ -85,5 +81,8 @@ private _clientTunnelSeqNumber; | ||
private _peerPort; | ||
private _connectionTimeoutTimer; | ||
private _heartbeatFailures; | ||
private _heartbeatRunning; | ||
private max_HeartbeatFailures; | ||
private _heartbeatTimer; | ||
private _discovery_timer; | ||
private _awaitingResponseType; | ||
@@ -93,41 +92,40 @@ private _clientSocket; | ||
private jKNXSecureKeyring; | ||
private_heartbeatRunning: boolean; | ||
private_clearToSend: boolean; | ||
private _timerTimeoutSendDisconnectRequestMessage; | ||
private _clearToSend; | ||
private timers; | ||
physAddr: KNXAddress; | ||
constructor(options: KNXClientOptions); | ||
get channelID(): number; | ||
get clearToSend(): boolean; | ||
private getKNXDataBuffer; | ||
private waitForEvent; | ||
private setTimer; | ||
private clearTimer; | ||
private clearAllTimers; | ||
getKNXDataBuffer(_data: Buffer, _dptid: string): KNXDataBuffer; | ||
send(knxPacket: KNXPacket): void; | ||
write(dstAddress: KNXAddress | string, data: any, dptid: string | number): void; | ||
respond(dstAddress: KNXAddress | string, data: Buffer, dptid: string | number): void; | ||
write(dstAddress: KNXAddress | string, data: any, dptid: any): void; | ||
respond(dstAddress: KNXAddress | string, data: Buffer, dptid: any): void; | ||
read(dstAddress: KNXAddress | string): void; | ||
writeRaw(dstAddress: KNXAddress | string, rawDataBuffer: Buffer, bitlength: number): void; | ||
private startHeartBeat; | ||
private stopHeartBeat; | ||
writeRaw(dstAddress: KNXAddress | string, _rawDataBuffer: Buffer, bitlength: number): void; | ||
startHeartBeat(): void; | ||
stopHeartBeat(): void; | ||
isDiscoveryRunning(): boolean; | ||
startDiscovery(): void; | ||
stopDiscovery(): void; | ||
static discover(eth?: string | number, timeout?: number): Promise<string[]>; | ||
static discover(eth?: string, timeout?: number): Promise<string[]>; | ||
Connect(knxLayer?: TunnelTypes): void; | ||
getConnectionStatus(): void; | ||
private closeSocket; | ||
Disconnect(): Promise<void>; | ||
isConnected(): boolean; | ||
private setDisconnected; | ||
private runHeartbeat; | ||
private getSeqNumber; | ||
private incSeqNumber; | ||
private setTimerWaitingForACK; | ||
private processInboundMessage; | ||
private sendDescriptionRequestMessage; | ||
private sendSearchRequestMessage; | ||
private sendConnectRequestMessage; | ||
private sendConnectionStateRequestMessage; | ||
private sendDisconnectRequestMessage; | ||
private sendDisconnectResponseMessage; | ||
private sendSecureSessionRequestMessage; | ||
_setDisconnected(_sReason?: string): Promise<void>; | ||
_runHeartbeat(): void; | ||
_getSeqNumber(): number; | ||
_getClearToSend(): boolean; | ||
_incSeqNumber(): number; | ||
_setTimerWaitingForACK(knxTunnelingRequest: KNXTunnelingRequest): void; | ||
_processInboundMessage(msg: Buffer, rinfo: RemoteInfo): void; | ||
_sendDescriptionRequestMessage(): void; | ||
_sendSearchRequestMessage(): void; | ||
_sendConnectRequestMessage(cri: any): void; | ||
_sendConnectionStateRequestMessage(channelID: any): void; | ||
_sendDisconnectRequestMessage(channelID: any): void; | ||
_sendDisconnectResponseMessage(channelID: any, status?: ConnectionStatus): void; | ||
_sendSecureSessionRequestMessage(cri: any): void; | ||
} |
@@ -29,3 +29,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.KNXTimer = exports.getDecodedKeyring = exports.KNXClientEvents = exports.SocketEvents = exports.ConncetionState = void 0; | ||
exports.getDecodedKeyring = exports.KNXClientEvents = exports.SocketEvents = exports.ConncetionState = void 0; | ||
const dgram_1 = __importDefault(require("dgram")); | ||
@@ -100,16 +100,5 @@ const net_1 = __importDefault(require("net")); | ||
exports.getDecodedKeyring = getDecodedKeyring; | ||
var KNXTimer; | ||
(function (KNXTimer) { | ||
KNXTimer["ACK"] = "ack"; | ||
KNXTimer["HEARTBEAT"] = "heartbeat"; | ||
KNXTimer["CONNECTION_STATE"] = "connection_state"; | ||
KNXTimer["CONNECTION"] = "connection"; | ||
KNXTimer["CONNECT_REQUEST"] = "connect_request"; | ||
KNXTimer["DISCONNECT"] = "disconnect"; | ||
KNXTimer["DISCOVERY"] = "discovery"; | ||
})(KNXTimer || (exports.KNXTimer = KNXTimer = {})); | ||
class KNXClient extends TypedEmitter_1.TypedEventEmitter { | ||
constructor(options) { | ||
super(); | ||
this.timers = new Map(); | ||
if (options === undefined) { | ||
@@ -127,2 +116,3 @@ options = optionsDefaults; | ||
this._connectionState = ConncetionState.DISCONNECTED; | ||
this._timerWaitingForACK = null; | ||
this._numFailedTelegramACK = 0; | ||
@@ -135,4 +125,7 @@ this._clientTunnelSeqNumber = -1; | ||
this._options.localSocketAddress = options.localSocketAddress; | ||
this._connectionTimeoutTimer = null; | ||
this._heartbeatFailures = 0; | ||
this.max_HeartbeatFailures = 3; | ||
this._heartbeatTimer = null; | ||
this._discovery_timer = null; | ||
this._awaitingResponseType = null; | ||
@@ -152,2 +145,3 @@ this._clientSocket = null; | ||
} | ||
this.removeAllListeners(); | ||
if (this._options.hostProtocol === 'TunnelUDP') { | ||
@@ -158,2 +152,3 @@ this._clientSocket = dgram_1.default.createSocket({ | ||
}); | ||
this._clientSocket.removeAllListeners(); | ||
this._clientSocket.bind({ port: null, address: this._options.localIPAddress }, () => { | ||
@@ -171,3 +166,3 @@ try { | ||
}); | ||
this._clientSocket.on(SocketEvents.message, this.processInboundMessage.bind(this)); | ||
this._clientSocket.on(SocketEvents.message, this._processInboundMessage.bind(this)); | ||
this._clientSocket.on(SocketEvents.error, (error) => this.emit(KNXClientEvents.error, error)); | ||
@@ -178,2 +173,3 @@ this._clientSocket.on(SocketEvents.close, () => this.emit(KNXClientEvents.close)); | ||
this._clientSocket = new net_1.default.Socket(); | ||
this._clientSocket.removeAllListeners(); | ||
this._clientSocket.on(SocketEvents.data, (data) => { | ||
@@ -190,4 +186,5 @@ console.log('Received message', data); | ||
}); | ||
this._clientSocket.removeAllListeners(); | ||
this._clientSocket.on(SocketEvents.listening, () => { }); | ||
this._clientSocket.on(SocketEvents.message, this.processInboundMessage.bind(this)); | ||
this._clientSocket.on(SocketEvents.message, this._processInboundMessage.bind(this)); | ||
this._clientSocket.on(SocketEvents.error, (error) => this.emit(KNXClientEvents.error, error)); | ||
@@ -218,12 +215,6 @@ this._clientSocket.on(SocketEvents.close, () => this.emit(KNXClientEvents.close)); | ||
} | ||
get clearToSend() { | ||
return this._clearToSend !== undefined ? this._clearToSend : true; | ||
} | ||
getKNXDataBuffer(data, dptid) { | ||
if (typeof dptid === 'number') { | ||
dptid = dptid.toString(); | ||
} | ||
getKNXDataBuffer(_data, _dptid) { | ||
const adpu = {}; | ||
DPTLib.populateAPDU(data, adpu, dptid); | ||
const iDatapointType = parseInt(dptid.substring(0, dptid.indexOf('.'))); | ||
DPTLib.populateAPDU(_data, adpu, _dptid); | ||
const iDatapointType = parseInt(_dptid.substr(0, _dptid.indexOf('.'))); | ||
const isSixBits = adpu.bitlength <= 6; | ||
@@ -243,38 +234,2 @@ this.sysLogger.trace(`isSixBits:${isSixBits} Includes (should be = isSixBits):${[ | ||
} | ||
async waitForEvent(event, timeout) { | ||
let resolveRef; | ||
return Promise.race([ | ||
new Promise((resolve) => { | ||
resolveRef = resolve; | ||
this.once(event, resolve); | ||
}), | ||
(0, utils_1.wait)(timeout), | ||
]).then(() => { | ||
this.off(event, resolveRef); | ||
}); | ||
} | ||
setTimer(type, cb, delay) { | ||
if (this.timers.has(type)) { | ||
clearTimeout(this.timers.get(type)); | ||
this.timers.delete(type); | ||
this.sysLogger.warn(`Timer "${type}" was already running`); | ||
} | ||
this.timers.set(type, setTimeout(() => { | ||
this.timers.delete(type); | ||
cb(); | ||
}, delay)); | ||
} | ||
clearTimer(type) { | ||
if (this.timers.has(type)) { | ||
clearTimeout(this.timers.get(type)); | ||
this.timers.delete(type); | ||
} | ||
} | ||
clearAllTimers() { | ||
this.stopDiscovery(); | ||
this.stopHeartBeat(); | ||
for (const timer of this.timers.keys()) { | ||
this.clearTimer(timer); | ||
} | ||
} | ||
send(knxPacket) { | ||
@@ -359,6 +314,6 @@ if (knxPacket instanceof KNXConnectRequest_1.default) { | ||
cEMIMessage.control.hopCount = 6; | ||
const seqNum = this.incSeqNumber(); | ||
const seqNum = this._incSeqNumber(); | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage); | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this._setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
@@ -393,6 +348,6 @@ if (this._options.localEchoInTunneling) | ||
cEMIMessage.control.hopCount = 6; | ||
const seqNum = this.incSeqNumber(); | ||
const seqNum = this._incSeqNumber(); | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage); | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this._setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
@@ -426,6 +381,6 @@ if (this._options.localEchoInTunneling) | ||
cEMIMessage.control.hopCount = 6; | ||
const seqNum = this.incSeqNumber(); | ||
const seqNum = this._incSeqNumber(); | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage); | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this._setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
@@ -436,6 +391,6 @@ if (this._options.localEchoInTunneling) | ||
} | ||
writeRaw(dstAddress, rawDataBuffer, bitlength) { | ||
writeRaw(dstAddress, _rawDataBuffer, bitlength) { | ||
if (this._connectionState !== ConncetionState.CONNECTED) | ||
throw new Error('The socket is not connected. Unable to access the KNX BUS'); | ||
if (!Buffer.isBuffer(rawDataBuffer)) { | ||
if (!Buffer.isBuffer(_rawDataBuffer)) { | ||
this.sysLogger.error('KNXClient: writeRaw: Value must be a buffer! '); | ||
@@ -454,3 +409,3 @@ return; | ||
const baseBufferFromBitLenght = Buffer.alloc(bitlength / 8); | ||
rawDataBuffer.copy(baseBufferFromBitLenght, 0); | ||
_rawDataBuffer.copy(baseBufferFromBitLenght, 0); | ||
const data = new KNXDataBuffer_1.default(baseBufferFromBitLenght, datapoint); | ||
@@ -479,6 +434,6 @@ if (typeof dstAddress === 'string') | ||
cEMIMessage.control.hopCount = 6; | ||
const seqNum = this.incSeqNumber(); | ||
const seqNum = this._incSeqNumber(); | ||
const knxPacketRequest = KNXProtocol_1.default.newKNXTunnelingRequest(this._channelID, seqNum, cEMIMessage); | ||
if (!this._options.suppress_ack_ldatareq) | ||
this.setTimerWaitingForACK(knxPacketRequest); | ||
this._setTimerWaitingForACK(knxPacketRequest); | ||
this.send(knxPacketRequest); | ||
@@ -493,11 +448,12 @@ if (this._options.localEchoInTunneling) | ||
this._heartbeatRunning = true; | ||
this.runHeartbeat(); | ||
this._runHeartbeat(); | ||
} | ||
stopHeartBeat() { | ||
this._heartbeatRunning = false; | ||
this.clearTimer(KNXTimer.HEARTBEAT); | ||
this.clearTimer(KNXTimer.CONNECTION_STATE); | ||
if (this._heartbeatTimer !== null) { | ||
this._heartbeatRunning = false; | ||
clearTimeout(this._heartbeatTimer); | ||
} | ||
} | ||
isDiscoveryRunning() { | ||
return this.timers.has(KNXTimer.DISCOVERY); | ||
return this._discovery_timer != null; | ||
} | ||
@@ -508,13 +464,16 @@ startDiscovery() { | ||
} | ||
this.setTimer(KNXTimer.DISCOVERY, () => { }, 1000 * KNXConstants_1.KNX_CONSTANTS.SEARCH_TIMEOUT); | ||
this.sendSearchRequestMessage(); | ||
this._discovery_timer = setTimeout(() => { | ||
this._discovery_timer = null; | ||
}, 1000 * KNXConstants_1.KNX_CONSTANTS.SEARCH_TIMEOUT); | ||
this._sendSearchRequestMessage(); | ||
} | ||
stopDiscovery() { | ||
this.clearTimer(KNXTimer.DISCOVERY); | ||
if (!this.isDiscoveryRunning()) { | ||
return; | ||
} | ||
if (this._discovery_timer !== null) | ||
clearTimeout(this._discovery_timer); | ||
this._discovery_timer = null; | ||
} | ||
static async discover(eth, timeout = 5000) { | ||
if (typeof eth === 'number') { | ||
timeout = eth; | ||
eth = undefined; | ||
} | ||
const client = new KNXClient({ | ||
@@ -530,2 +489,3 @@ interface: eth, | ||
await (0, utils_1.wait)(timeout); | ||
client.stopDiscovery(); | ||
await client.Disconnect(); | ||
@@ -550,7 +510,9 @@ return discovered; | ||
this._clearToSend = true; | ||
this.clearTimer(KNXTimer.CONNECTION); | ||
if (this._connectionTimeoutTimer !== null) | ||
clearTimeout(this._connectionTimeoutTimer); | ||
this.emit(KNXClientEvents.connecting, this._options); | ||
if (this._options.hostProtocol === 'TunnelUDP') { | ||
const timeoutError = new Error(`Connection timeout to ${this._peerHost}:${this._peerPort}`); | ||
this.setTimer(KNXTimer.CONNECTION, () => { | ||
this._connectionTimeoutTimer = setTimeout(() => { | ||
this._connectionTimeoutTimer = null; | ||
this.emit(KNXClientEvents.error, timeoutError); | ||
@@ -560,7 +522,8 @@ }, 1000 * KNXConstants_1.KNX_CONSTANTS.CONNECT_REQUEST_TIMEOUT); | ||
this._clientTunnelSeqNumber = -1; | ||
this.setTimer(KNXTimer.CONNECT_REQUEST, () => { | ||
this.sendConnectRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
const t = setTimeout(() => { | ||
this._sendConnectRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
}, 2000); | ||
} | ||
else if (this._options.hostProtocol === 'TunnelTCP') { | ||
const timeoutError = new Error(`Connection timeout to ${this._peerHost}:${this._peerPort}`); | ||
this._clientSocket.connect(this._peerPort, this._peerHost, () => { | ||
@@ -570,3 +533,3 @@ this._awaitingResponseType = KNXConstants_1.KNX_CONSTANTS.CONNECT_RESPONSE; | ||
if (this._options.isSecureKNXEnabled) | ||
this.sendSecureSessionRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
this._sendSecureSessionRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
}); | ||
@@ -582,2 +545,21 @@ } | ||
} | ||
getConnectionStatus() { | ||
if (this._clientSocket == null) { | ||
throw new Error('No client socket defined'); | ||
} | ||
const timeoutError = new Error(`HeartBeat failure with ${this._peerHost}:${this._peerPort}`); | ||
const deadError = new Error(`Connection dead with ${this._peerHost}:${this._peerPort}`); | ||
this._heartbeatTimer = setTimeout(() => { | ||
this._heartbeatTimer = null; | ||
this.sysLogger.error(`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`); | ||
this._heartbeatFailures++; | ||
if (this._heartbeatFailures >= this.max_HeartbeatFailures) { | ||
this._heartbeatFailures = 0; | ||
this.emit(KNXClientEvents.error, deadError); | ||
this._setDisconnected(deadError.message); | ||
} | ||
}, 1000 * KNXConstants_1.KNX_CONSTANTS.CONNECTIONSTATE_REQUEST_TIMEOUT); | ||
this._awaitingResponseType = KNXConstants_1.KNX_CONSTANTS.CONNECTIONSTATE_RESPONSE; | ||
this._sendConnectionStateRequestMessage(this._channelID); | ||
} | ||
async closeSocket() { | ||
@@ -605,7 +587,2 @@ return new Promise((resolve) => { | ||
} | ||
if (this._connectionState === ConncetionState.DISCONNECTING) { | ||
throw new Error('Already disconnecting'); | ||
} | ||
this.clearAllTimers(); | ||
this._connectionState = ConncetionState.DISCONNECTING; | ||
if (this._channelID === null) { | ||
@@ -616,7 +593,9 @@ this.sysLogger.debug(`KNXClient: into Disconnect(), channel id is not defined so skip disconnect packet and close socket`); | ||
} | ||
this.stopHeartBeat(); | ||
this._connectionState = ConncetionState.DISCONNECTING; | ||
this._awaitingResponseType = KNXConstants_1.KNX_CONSTANTS.DISCONNECT_RESPONSE; | ||
this.sendDisconnectRequestMessage(this._channelID); | ||
await this.waitForEvent(KNXClientEvents.disconnected, 2000); | ||
this._sendDisconnectRequestMessage(this._channelID); | ||
await (0, utils_1.wait)(2000); | ||
if (this._connectionState !== ConncetionState.DISCONNECTED) { | ||
this.setDisconnected("Forced call from KNXClient Disconnect() function, because the KNX Interface hasn't sent the DISCONNECT_RESPONSE in time."); | ||
this._setDisconnected("Forced call from KNXClient Disconnect() function, because the KNX Interface hasn't sent the DISCONNECT_RESPONSE in time."); | ||
} | ||
@@ -627,6 +606,11 @@ } | ||
} | ||
async setDisconnected(_sReason = '') { | ||
async _setDisconnected(_sReason = '') { | ||
this.sysLogger.debug(`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`); | ||
this._connectionState = ConncetionState.DISCONNECTED; | ||
this.clearAllTimers(); | ||
this.stopHeartBeat(); | ||
this._timerTimeoutSendDisconnectRequestMessage = null; | ||
if (this._connectionTimeoutTimer !== null) | ||
clearTimeout(this._connectionTimeoutTimer); | ||
if (this._timerWaitingForACK !== null) | ||
clearTimeout(this._timerWaitingForACK); | ||
this._clientTunnelSeqNumber = -1; | ||
@@ -638,29 +622,17 @@ this._channelID = null; | ||
} | ||
runHeartbeat() { | ||
if (!this._heartbeatRunning) { | ||
return; | ||
_runHeartbeat() { | ||
if (this._heartbeatRunning) { | ||
this.getConnectionStatus(); | ||
const t = setTimeout(() => { | ||
this._runHeartbeat(); | ||
}, 1000 * this._options.connectionKeepAliveTimeout); | ||
} | ||
if (this._clientSocket == null) { | ||
throw new Error('No client socket defined'); | ||
} | ||
const deadError = new Error(`Connection dead with ${this._peerHost}:${this._peerPort}`); | ||
this.setTimer(KNXTimer.CONNECTION_STATE, () => { | ||
this.sysLogger.error(`KNXClient: getConnectionStatus Timeout ${this._heartbeatFailures} out of ${this.max_HeartbeatFailures}`); | ||
this._heartbeatFailures++; | ||
if (this._heartbeatFailures >= this.max_HeartbeatFailures) { | ||
this._heartbeatFailures = 0; | ||
this.emit(KNXClientEvents.error, deadError); | ||
this.setDisconnected(deadError.message); | ||
} | ||
}, 1000 * KNXConstants_1.KNX_CONSTANTS.CONNECTIONSTATE_REQUEST_TIMEOUT); | ||
this._awaitingResponseType = KNXConstants_1.KNX_CONSTANTS.CONNECTIONSTATE_RESPONSE; | ||
this.sendConnectionStateRequestMessage(this._channelID); | ||
this.setTimer(KNXTimer.HEARTBEAT, () => { | ||
this.runHeartbeat(); | ||
}, 1000 * this._options.connectionKeepAliveTimeout); | ||
} | ||
getSeqNumber() { | ||
_getSeqNumber() { | ||
return this._clientTunnelSeqNumber; | ||
} | ||
incSeqNumber() { | ||
_getClearToSend() { | ||
return this._clearToSend !== undefined ? this._clearToSend : true; | ||
} | ||
_incSeqNumber() { | ||
this._clientTunnelSeqNumber++; | ||
@@ -672,7 +644,9 @@ if (this._clientTunnelSeqNumber > 255) { | ||
} | ||
setTimerWaitingForACK(knxTunnelingRequest) { | ||
_setTimerWaitingForACK(knxTunnelingRequest) { | ||
this._clearToSend = false; | ||
const timeoutErr = new errors.RequestTimeoutError(`seqCounter:${knxTunnelingRequest.seqCounter}, DestAddr:${knxTunnelingRequest.cEMIMessage.dstAddress.toString() || | ||
'Non definito'}, AckRequested:${knxTunnelingRequest.cEMIMessage.control.ack}, timed out waiting telegram acknowledge by ${this._options.ipAddr || 'No Peer host detected'}`); | ||
this.setTimer(KNXTimer.ACK, () => { | ||
if (this._timerWaitingForACK !== null) | ||
clearTimeout(this._timerWaitingForACK); | ||
this._timerWaitingForACK = setTimeout(() => { | ||
this._numFailedTelegramACK += 1; | ||
@@ -684,12 +658,12 @@ if (this._numFailedTelegramACK > 2) { | ||
this.emit(KNXClientEvents.error, timeoutErr); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. ABORT sending datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. ABORT sending datagram with seqNumber ${this._getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
} | ||
else { | ||
this.setTimerWaitingForACK(knxTunnelingRequest); | ||
this._setTimerWaitingForACK(knxTunnelingRequest); | ||
this.send(knxTunnelingRequest); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. Retransmit datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
this.sysLogger.error(`KNXClient: _setTimerWaitingForACK: ${timeoutErr.message || 'Undef error'} no ACK received. Retransmit datagram with seqNumber ${this._getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`); | ||
} | ||
}, KNXConstants_1.KNX_CONSTANTS.TUNNELING_REQUEST_TIMEOUT * 1000); | ||
} | ||
processInboundMessage(msg, rinfo) { | ||
_processInboundMessage(msg, rinfo) { | ||
let sProcessInboundLog = ''; | ||
@@ -712,4 +686,5 @@ try { | ||
if (knxHeader.service_type === KNXConstants_1.KNX_CONSTANTS.SEARCH_RESPONSE) { | ||
if (!this.isDiscoveryRunning()) | ||
if (this._discovery_timer == null) { | ||
return; | ||
} | ||
this.emit(KNXClientEvents.discover, `${rinfo.address}:${rinfo.port}`, knxHeader, knxMessage); | ||
@@ -719,3 +694,5 @@ } | ||
if (this._connectionState === ConncetionState.CONNECTING) { | ||
this.clearTimer(KNXTimer.CONNECTION); | ||
if (this._connectionTimeoutTimer !== null) | ||
clearTimeout(this._connectionTimeoutTimer); | ||
this._connectionTimeoutTimer = null; | ||
const knxConnectResponse = knxMessage; | ||
@@ -725,6 +702,7 @@ if (knxConnectResponse.status !== | ||
this.emit(KNXClientEvents.error, Error(KNXConnectResponse_1.default.statusToString(knxConnectResponse.status))); | ||
this.setDisconnected(`Connect response error ${knxConnectResponse.status}`); | ||
this._setDisconnected(`Connect response error ${knxConnectResponse.status}`); | ||
return; | ||
} | ||
this.clearTimer(KNXTimer.ACK); | ||
if (this._timerWaitingForACK !== null) | ||
clearTimeout(this._timerWaitingForACK); | ||
this._channelID = knxConnectResponse.channelID; | ||
@@ -744,3 +722,3 @@ this._connectionState = ConncetionState.CONNECTED; | ||
} | ||
this.setDisconnected('Received DISCONNECT_RESPONSE from the KNX interface.'); | ||
this._setDisconnected('Received DISCONNECT_RESPONSE from the KNX interface.'); | ||
} | ||
@@ -754,5 +732,5 @@ else if (knxHeader.service_type === KNXConstants_1.KNX_CONSTANTS.DISCONNECT_REQUEST) { | ||
this._connectionState = ConncetionState.DISCONNECTING; | ||
this.sendDisconnectResponseMessage(knxDisconnectRequest.channelID); | ||
this.setTimer(KNXTimer.DISCONNECT, () => { | ||
this.setDisconnected(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
this._sendDisconnectResponseMessage(knxDisconnectRequest.channelID); | ||
const t = setTimeout(() => { | ||
this._setDisconnected(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
}, 1000); | ||
@@ -793,4 +771,5 @@ } | ||
if (!this._options.suppress_ack_ldatareq) { | ||
if (knxTunnelingAck.seqCounter === this.getSeqNumber()) { | ||
this.clearTimer(KNXTimer.ACK); | ||
if (knxTunnelingAck.seqCounter === this._getSeqNumber()) { | ||
if (this._timerWaitingForACK !== null) | ||
clearTimeout(this._timerWaitingForACK); | ||
this._numFailedTelegramACK = 0; | ||
@@ -831,12 +810,12 @@ this._clearToSend = true; | ||
this.emit(KNXClientEvents.error, Error(KNXConnectionStateResponse_1.default.statusToString(knxConnectionStateResponse.status))); | ||
this.setDisconnected(`Awaiting response ${this._awaitingResponseType}, received connection state response with status ${knxConnectionStateResponse.status}`); | ||
this._setDisconnected(`Awaiting response ${this._awaitingResponseType}, received connection state response with status ${knxConnectionStateResponse.status}`); | ||
} | ||
else { | ||
this.clearTimer(KNXTimer.CONNECTION_STATE); | ||
if (this._heartbeatTimer !== null) | ||
clearTimeout(this._heartbeatTimer); | ||
this._heartbeatFailures = 0; | ||
} | ||
} | ||
else { | ||
this.clearTimer(KNXTimer.CONNECTION); | ||
} | ||
else if (this._connectionTimeoutTimer !== null) | ||
clearTimeout(this._connectionTimeoutTimer); | ||
} | ||
@@ -850,21 +829,21 @@ this.emit(KNXClientEvents.response, `${rinfo.address}:${rinfo.port}`, knxHeader, knxMessage); | ||
} | ||
sendDescriptionRequestMessage() { | ||
_sendDescriptionRequestMessage() { | ||
this.send(KNXProtocol_1.default.newKNXDescriptionRequest(new HPAI_1.default(this._options.localIPAddress))); | ||
} | ||
sendSearchRequestMessage() { | ||
_sendSearchRequestMessage() { | ||
this.send(KNXProtocol_1.default.newKNXSearchRequest(new HPAI_1.default(this._options.localIPAddress, this._peerPort))); | ||
} | ||
sendConnectRequestMessage(cri) { | ||
_sendConnectRequestMessage(cri) { | ||
this.send(KNXProtocol_1.default.newKNXConnectRequest(cri)); | ||
} | ||
sendConnectionStateRequestMessage(channelID) { | ||
_sendConnectionStateRequestMessage(channelID) { | ||
this.send(KNXProtocol_1.default.newKNXConnectionStateRequest(channelID)); | ||
} | ||
sendDisconnectRequestMessage(channelID) { | ||
_sendDisconnectRequestMessage(channelID) { | ||
this.send(KNXProtocol_1.default.newKNXDisconnectRequest(channelID)); | ||
} | ||
sendDisconnectResponseMessage(channelID, status = KNXConstants_1.ConnectionStatus.E_NO_ERROR) { | ||
_sendDisconnectResponseMessage(channelID, status = KNXConstants_1.ConnectionStatus.E_NO_ERROR) { | ||
this.send(KNXProtocol_1.default.newKNXDisconnectResponse(channelID, status)); | ||
} | ||
sendSecureSessionRequestMessage(cri) { | ||
_sendSecureSessionRequestMessage(cri) { | ||
const oHPAI = new HPAI_1.default('0.0.0.0', 0, this._options.hostProtocol === 'TunnelTCP' | ||
@@ -871,0 +850,0 @@ ? KNXConstants_1.KNX_CONSTANTS.IPV4_TCP |
@@ -13,2 +13,9 @@ | ||
<p> | ||
<b>Version 2.1.3</b> - May 2024<br/> | ||
- FIX: fixed datapoint 9 issue.<br/> | ||
</p> | ||
# CHANGELOG | ||
<p> | ||
<b>Version 1.0.47</b> - January 2024<br/> | ||
@@ -15,0 +22,0 @@ - NEW: added DPT 275.100.<br/> |
{ | ||
"name": "knxultimate", | ||
"description": "KNX IP protocol implementation for Node. This is the ENGINE of Node-Red KNX-Ultimate node.", | ||
"version": "2.1.2", | ||
"version": "2.1.3", | ||
"main": "./build/index.js", | ||
@@ -94,2 +94,2 @@ "engines": { | ||
} | ||
} | ||
} |
@@ -16,3 +16,2 @@ ![Logo](img/logo-big.png) | ||
> I had many users asking for a node.js release of that engine, so here is it. | ||
> The node will be KNX Secure compatible. I'm already working on that. | ||
@@ -24,7 +23,19 @@ ## CHANGELOG | ||
**Properties to be passed to the connection(see the knxUltimateClientProperties variable below)** | ||
|Technology|Supported| | ||
|--|--| | ||
| KNX Tunnelling | ![](https://placehold.co/200x20/green/white?text=YES) | | ||
| KNX Routing | ![](https://placehold.co/200x20/green/white?text=YES) | | ||
| KNX Secure Tunnelling | ![](https://placehold.co/200x20/orange/white?text=UNDER+DEVELOPMENT) | | ||
| KNX Secure Routing | ![](https://placehold.co/200x20/red/white?text=NO) | | ||
| KNX 3rd PARTY IOT API | ![](https://placehold.co/200x20/dimgrey/white?text=INVESTIGATING) | | ||
| KNX MATTER | ![](https://placehold.co/200x20/dimgrey/white?text=INVESTIGATING) | | ||
## CONNECTION SETUP | ||
These are the properties to be passed to the connection as a *JSON object {}* (see the knxUltimateClientProperties variable in the exsamples) | ||
|Property|Description| | ||
|--|--| | ||
| ipAddr (string) | The IP of your KNX router/interface (for Routers, use "224.0.23.12") | | ||
| hostProtocol (string) | "Multicast" if you're connecting to a KNX Router. "TunnelUDP" for KNX Interfaces, or "TunnelTCP" for secure KNX Interfaces (**KNX Secure is not yet implemented**)| | ||
| ipPort (string) | The port, default is "3671" | | ||
@@ -35,10 +46,10 @@ | physAddr (string) | The physical address to be identified in the KNX bus | | ||
| localEchoInTunneling (bool) | Leave true forever. This is used only in Node-Red KNX-Ultimate node | | ||
| hostProtocol (string) | "Multicast" if you're connecting to a KNX Router. "TunnelUDP" for KNX Interfaces, or "TunnelTCP" for secure KNX Interfaces (not yet implemented)| | ||
| isSecureKNXEnabled (bool) | True: Enables the secure connection. Leave false until KNX-Secure has been released. | | ||
| jKNXSecureKeyring (string) | ETS Keyring JSON file content (leave blank until KNX-Secure has been released) | | ||
| isSecureKNXEnabled (bool) | True: Enables the secure connection. **Leave false until KNX-Secure has been released**. | | ||
| jKNXSecureKeyring (string) | ETS Keyring JSON file content. **Leave blank until KNX-Secure has been released**. | | ||
| localIPAddress (string) | The local IP address to be used to connect to the KNX/IP Bus. Leave blank, will be automatically filled by KNXUltimate | | ||
| interface (string) | Specifies the local eth interface to be used to connect to the KNX Bus.| | ||
**Supported Datapoints** | ||
## SUPPORTED DATAPOINTS | ||
For each Datapoint, there is a sample on how to format the payload (telegram) to be passed.<br/> | ||
@@ -52,3 +63,3 @@ For example, pass a *true* for datapoint "1.001", or *{ red: 125, green: 0, blue: 0 }* for datapoint "232.600".<br/> | ||
## CONTROL THE CLIENT | ||
## METHODS/PROPERTIES OF KNXULTIMATE | ||
@@ -64,3 +75,3 @@ |Method|Description| | ||
|Properties|Description| | ||
|Property|Description| | ||
|--|--| | ||
@@ -73,4 +84,14 @@ | .isConnected() | Returns **true** if you the client is connected to the KNX Gateway Router/Interface, **false** if not connected. | | ||
Please see the sample.js file. This sample contains all events triggered by KNXUltimate. | ||
List of events raised by KNXultimate, in proper order. For the signatures, please see the **examples** folder. | ||
|Event|Description| | ||
|--|--| | ||
| connecting | KNXUltimate is connecting to the KNX/IP Gateway. Please wait for the *connected* event to start sending KNX telegrams.| | ||
| connected | KNXUltimate has successfully connected with the KNX/IP Gateway. | | ||
| indication | KNXUltimate has received a KNX telegram, that's avaiable in te the **datagram** variable. Please see the examples. | | ||
| ackReceived | Ack telegram from KNX/IP Gateway has been received. This confirms that the telegram sent by KNXUltimate has reached the KNX/IP Gateway successfully. | | ||
| disconnected | The KNX connection has been disconnected. | | ||
| close | The main KNXUltimate socket has been closed. | | ||
| error | KNXUltimate has raised an error. The error description is provided as well. | | ||
## DECONDING THE TELEGRAMS FROM BUS | ||
@@ -87,3 +108,3 @@ | ||
## Examples | ||
## EXAMPLES | ||
@@ -98,2 +119,14 @@ You can find all examples in the [examples](./examples/) folder: | ||
<br/> | ||
## HOW TO COLLABORATE | ||
If you want to help us in this project, you're wellcome! | ||
Please refer to the development page. | ||
* [Development's page](https://github.com/Supergiovane/knxultimate/blob/master/DEVELOPMENT.md) | ||
<br/> | ||
## SUGGESTION | ||
@@ -100,0 +133,0 @@ > |
@@ -15,3 +15,3 @@ /** | ||
const config: DatapointConfig = { | ||
id: 'DPT2', | ||
id: 'DPT9', | ||
formatAPDU: (value) => { | ||
@@ -18,0 +18,0 @@ const apdu_data = Buffer.alloc(2) |
Sorry, the diff of this file is not supported yet
1636661
679
30705
145
4