knxultimate
Advanced tools
Comparing version 2.1.1 to 2.1.2
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { RemoteInfo } from 'dgram'; | ||
import { ConnectionStatus } from './protocol/KNXConstants'; | ||
import { KnxResponse } from './protocol/KNXProtocol'; | ||
import TunnelCRI, { TunnelTypes } from './protocol/TunnelCRI'; | ||
import { TunnelTypes } from './protocol/TunnelCRI'; | ||
import KNXAddress from './protocol/KNXAddress'; | ||
import KNXDataBuffer from './protocol/KNXDataBuffer'; | ||
import { KNXLoggerOptions } from './KnxLog'; | ||
@@ -92,8 +88,5 @@ import { KNXPacket } from './protocol'; | ||
private _awaitingResponseType; | ||
private _awaitingResponsePromise; | ||
private _clientSocket; | ||
private sysLogger; | ||
private jKNXSecureKeyring; | ||
private_heartbeatRunning: boolean; | ||
private_clearToSend: boolean; | ||
private _clearToSend; | ||
@@ -104,3 +97,4 @@ private timers; | ||
get channelID(): number; | ||
getKNXDataBuffer(_data: Buffer, _dptid: string): KNXDataBuffer; | ||
get clearToSend(): boolean; | ||
private getKNXDataBuffer; | ||
private waitForEvent; | ||
@@ -111,12 +105,12 @@ private setTimer; | ||
send(knxPacket: KNXPacket): void; | ||
write(dstAddress: KNXAddress | string, data: any, dptid: any): void; | ||
respond(dstAddress: KNXAddress | string, data: Buffer, dptid: any): void; | ||
write(dstAddress: KNXAddress | string, data: any, dptid: string | number): void; | ||
respond(dstAddress: KNXAddress | string, data: Buffer, dptid: string | number): void; | ||
read(dstAddress: KNXAddress | string): void; | ||
writeRaw(dstAddress: KNXAddress | string, _rawDataBuffer: Buffer, bitlength: number): void; | ||
startHeartBeat(): void; | ||
stopHeartBeat(): void; | ||
writeRaw(dstAddress: KNXAddress | string, rawDataBuffer: Buffer, bitlength: number): void; | ||
private startHeartBeat; | ||
private stopHeartBeat; | ||
isDiscoveryRunning(): boolean; | ||
startDiscovery(): void; | ||
stopDiscovery(): void; | ||
static discover(timeout?: number): Promise<string[]>; | ||
static discover(eth?: string | number, timeout?: number): Promise<string[]>; | ||
Connect(knxLayer?: TunnelTypes): void; | ||
@@ -126,16 +120,15 @@ private closeSocket; | ||
isConnected(): boolean; | ||
_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: TunnelCRI): void; | ||
_sendConnectionStateRequestMessage(channelID: number): void; | ||
_sendDisconnectRequestMessage(channelID: number): void; | ||
_sendDisconnectResponseMessage(channelID: number, status?: ConnectionStatus): void; | ||
_sendSecureSessionRequestMessage(cri: TunnelCRI): void; | ||
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; | ||
} |
@@ -148,3 +148,2 @@ "use strict"; | ||
} | ||
this.removeAllListeners(); | ||
if (this._options.hostProtocol === 'TunnelUDP') { | ||
@@ -155,3 +154,2 @@ this._clientSocket = dgram_1.default.createSocket({ | ||
}); | ||
this._clientSocket.removeAllListeners(); | ||
this._clientSocket.bind({ port: null, address: this._options.localIPAddress }, () => { | ||
@@ -169,3 +167,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)); | ||
@@ -176,3 +174,2 @@ 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) => { | ||
@@ -189,5 +186,4 @@ 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,6 +214,12 @@ this._clientSocket.on(SocketEvents.close, () => this.emit(KNXClientEvents.close)); | ||
} | ||
getKNXDataBuffer(_data, _dptid) { | ||
get clearToSend() { | ||
return this._clearToSend !== undefined ? this._clearToSend : true; | ||
} | ||
getKNXDataBuffer(data, dptid) { | ||
if (typeof dptid === 'number') { | ||
dptid = dptid.toString(); | ||
} | ||
const adpu = {}; | ||
DPTLib.populateAPDU(_data, adpu, _dptid); | ||
const iDatapointType = parseInt(_dptid.substr(0, _dptid.indexOf('.'))); | ||
DPTLib.populateAPDU(data, adpu, dptid); | ||
const iDatapointType = parseInt(dptid.substring(0, dptid.indexOf('.'))); | ||
const isSixBits = adpu.bitlength <= 6; | ||
@@ -238,8 +240,12 @@ 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); | ||
}); | ||
} | ||
@@ -349,6 +355,6 @@ setTimer(type, cb, delay) { | ||
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); | ||
@@ -383,6 +389,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); | ||
@@ -416,6 +422,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 +432,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! '); | ||
@@ -444,3 +450,3 @@ return; | ||
const baseBufferFromBitLenght = Buffer.alloc(bitlength / 8); | ||
_rawDataBuffer.copy(baseBufferFromBitLenght, 0); | ||
rawDataBuffer.copy(baseBufferFromBitLenght, 0); | ||
const data = new KNXDataBuffer_1.default(baseBufferFromBitLenght, datapoint); | ||
@@ -469,6 +475,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); | ||
@@ -483,3 +489,3 @@ if (this._options.localEchoInTunneling) | ||
this._heartbeatRunning = true; | ||
this._runHeartbeat(); | ||
this.runHeartbeat(); | ||
} | ||
@@ -499,3 +505,3 @@ stopHeartBeat() { | ||
this.setTimer(KNXTimer.DISCOVERY, () => { }, 1000 * KNXConstants_1.KNX_CONSTANTS.SEARCH_TIMEOUT); | ||
this._sendSearchRequestMessage(); | ||
this.sendSearchRequestMessage(); | ||
} | ||
@@ -549,3 +555,3 @@ stopDiscovery() { | ||
this.setTimer(KNXTimer.CONNECT_REQUEST, () => { | ||
this._sendConnectRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
this.sendConnectRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
}, 2000); | ||
@@ -558,3 +564,3 @@ } | ||
if (this._options.isSecureKNXEnabled) | ||
this._sendSecureSessionRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
this.sendSecureSessionRequestMessage(new TunnelCRI_1.default(knxLayer)); | ||
}); | ||
@@ -603,6 +609,6 @@ } | ||
this._awaitingResponseType = KNXConstants_1.KNX_CONSTANTS.DISCONNECT_RESPONSE; | ||
this._sendDisconnectRequestMessage(this._channelID); | ||
this.sendDisconnectRequestMessage(this._channelID); | ||
await this.waitForEvent(KNXClientEvents.disconnected, 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."); | ||
} | ||
@@ -613,3 +619,3 @@ } | ||
} | ||
async _setDisconnected(_sReason = '') { | ||
async setDisconnected(_sReason = '') { | ||
this.sysLogger.debug(`KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`); | ||
@@ -624,3 +630,3 @@ this._connectionState = ConncetionState.DISCONNECTED; | ||
} | ||
_runHeartbeat() { | ||
runHeartbeat() { | ||
if (!this._heartbeatRunning) { | ||
@@ -639,18 +645,15 @@ return; | ||
this.emit(KNXClientEvents.error, deadError); | ||
this._setDisconnected(deadError.message); | ||
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.sendConnectionStateRequestMessage(this._channelID); | ||
this.setTimer(KNXTimer.HEARTBEAT, () => { | ||
this._runHeartbeat(); | ||
this.runHeartbeat(); | ||
}, 1000 * this._options.connectionKeepAliveTimeout); | ||
} | ||
_getSeqNumber() { | ||
getSeqNumber() { | ||
return this._clientTunnelSeqNumber; | ||
} | ||
_getClearToSend() { | ||
return this._clearToSend !== undefined ? this._clearToSend : true; | ||
} | ||
_incSeqNumber() { | ||
incSeqNumber() { | ||
this._clientTunnelSeqNumber++; | ||
@@ -662,3 +665,3 @@ if (this._clientTunnelSeqNumber > 255) { | ||
} | ||
_setTimerWaitingForACK(knxTunnelingRequest) { | ||
setTimerWaitingForACK(knxTunnelingRequest) { | ||
this._clearToSend = false; | ||
@@ -674,12 +677,12 @@ const timeoutErr = new errors.RequestTimeoutError(`seqCounter:${knxTunnelingRequest.seqCounter}, DestAddr:${knxTunnelingRequest.cEMIMessage.dstAddress.toString() || | ||
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 = ''; | ||
@@ -713,3 +716,3 @@ try { | ||
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; | ||
@@ -732,3 +735,3 @@ } | ||
} | ||
this._setDisconnected('Received DISCONNECT_RESPONSE from the KNX interface.'); | ||
this.setDisconnected('Received DISCONNECT_RESPONSE from the KNX interface.'); | ||
} | ||
@@ -742,5 +745,5 @@ else if (knxHeader.service_type === KNXConstants_1.KNX_CONSTANTS.DISCONNECT_REQUEST) { | ||
this._connectionState = ConncetionState.DISCONNECTING; | ||
this._sendDisconnectResponseMessage(knxDisconnectRequest.channelID); | ||
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.setDisconnected(`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`); | ||
}, 1000); | ||
@@ -781,3 +784,3 @@ } | ||
if (!this._options.suppress_ack_ldatareq) { | ||
if (knxTunnelingAck.seqCounter === this._getSeqNumber()) { | ||
if (knxTunnelingAck.seqCounter === this.getSeqNumber()) { | ||
this.clearTimer(KNXTimer.ACK); | ||
@@ -819,3 +822,3 @@ this._numFailedTelegramACK = 0; | ||
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}`); | ||
} | ||
@@ -838,21 +841,21 @@ else { | ||
} | ||
_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' | ||
@@ -859,0 +862,0 @@ ? KNXConstants_1.KNX_CONSTANTS.IPV4_TCP |
## [2.1.2](https://github.com/Supergiovane/KNXUltimate/compare/v2.1.1...v2.1.2) (2024-04-24) | ||
### Bug Fixes | ||
* refactor method names and added js docs ([#19](https://github.com/Supergiovane/KNXUltimate/issues/19)) ([432db89](https://github.com/Supergiovane/KNXUltimate/commit/432db89ac4a43be1a8037c4dbcd3cee8fe311e10)) | ||
* wrong overload of `discovery` method ([a41b65c](https://github.com/Supergiovane/KNXUltimate/commit/a41b65c537890177728708ec6d64282bab9df1ef)) | ||
## [2.1.1](https://github.com/Supergiovane/KNXUltimate/compare/v2.1.0...v2.1.1) (2024-04-23) | ||
@@ -4,0 +12,0 @@ |
{ | ||
"name": "knxultimate", | ||
"description": "KNX IP protocol implementation for Node. This is the ENGINE of Node-Red KNX-Ultimate node.", | ||
"version": "2.1.1", | ||
"version": "2.1.2", | ||
"main": "./build/index.js", | ||
@@ -6,0 +6,0 @@ "engines": { |
330
README.md
![Logo](img/logo-big.png) | ||
<br/> | ||
[![CI](https://github.com/Supergiovane/KNXUltimate/actions/workflows/ci.yml/badge.svg)](https://github.com/Supergiovane/KNXUltimate/actions/workflows/ci.yml) | ||
[![NPM version][npm-version-image]][npm-url] | ||
@@ -13,4 +12,2 @@ [![NPM downloads per month][npm-downloads-month-image]][npm-url] | ||
<br/> | ||
Control your KNX intallation via Node.js! | ||
@@ -22,5 +19,2 @@ | ||
<br/> | ||
<br/> | ||
## CHANGELOG | ||
@@ -31,5 +25,2 @@ | ||
<br/> | ||
<br/> | ||
**Properties to be passed to the connection(see the knxUltimateClientProperties variable below)** | ||
@@ -51,5 +42,2 @@ | ||
<br/> | ||
<br/> | ||
**Supported Datapoints** | ||
@@ -65,5 +53,2 @@ | ||
<br/> | ||
<br/> | ||
## CONTROL THE CLIENT | ||
@@ -76,16 +61,12 @@ | ||
| .write (GA, payload, datapoint) | Sends a WRITE telegram to the BUS. **GA** is the group address (for example "0/0/1"), **payload** is the value you want to send (for example true), **datapoint** is a string representing the datapoint (for example "5.001") | | ||
| .writeRaw (GA, payload, datapoint) | Sends a WRITE telegram to the BUS. **GA** is the group address (for example "0/0/1"), **payload** is the buffer you want to send, **datapoint** is a string representing the datapoint (for example "5.001") | | ||
| .respond (GA, payload, datapoint) | Sends a RESPONSE telegram to the BUS. **GA** is the group address (for example "0/0/1"), **payload** is the value you want to send (for example true), **datapoint** is a string representing the datapoint (for example "5.001") | | ||
| .read (GA) | Sends a READ telegram to the BUS. **GA** is the group address (for example "0/0/1").| | ||
<br/> | ||
<br/> | ||
|Properties|Description| | ||
|--|--| | ||
| .isConnected() | Returns **true** if you the client is connected to the KNX Gateway Router/Interface, **false** if not connected. | | ||
| ._getClearToSend() | Returns **true** if you can send a telegram, **false** if the client is still waiting for the last telegram's ACK or whenever the client cannot temporary send the telegram. In tunneling mode, you could also refer to the event **KNXClientEvents.ackReceived**, that is fired everytime a telegram has been succesfully acknowledge or not acknowledge. See the sample.js file. | | ||
| .clearToSend | **true** if you can send a telegram, **false** if the client is still waiting for the last telegram's ACK or whenever the client cannot temporary send the telegram. In tunneling mode, you could also refer to the event **KNXClientEvents.ackReceived**, that is fired everytime a telegram has been succesfully acknowledge or not acknowledge. See the sample.js file. | | ||
| .channelID | The actual Channel ID. Only defined after a successfull connection | | ||
<br/> | ||
<br/> | ||
## EVENTS | ||
@@ -95,5 +76,2 @@ | ||
<br/> | ||
<br/> | ||
## DECONDING THE TELEGRAMS FROM BUS | ||
@@ -105,3 +83,3 @@ | ||
```javascript | ||
const dptlib = require('./src/dptlib'); | ||
import { dptlib } from "knxultimate"; | ||
let dpt = dptlib.resolve("1.001"); | ||
@@ -111,296 +89,12 @@ let jsValue = dptlib.fromBuffer(RAW VALUE (SEE SAMPLES), dpt); // THIS IS THE DECODED VALUE | ||
<br/> | ||
<br/> | ||
## Examples | ||
## Simple sample (you can find this sample in the "simpleSample.js" file) | ||
You can find all examples in the [examples](./examples/) folder: | ||
```javascript | ||
const knx = require("./index.js"); | ||
const dptlib = require('./src/dptlib'); | ||
* [sample](./examples/sample.ts) - A full featured example that shows how to connect to the KNX bus and send/receive telegrams. | ||
* [simpleSample](./examples/simpleSample.ts) - A simple example that shows how to connect to the KNX bus and send a telegram. | ||
* [discovery](./examples/discovery.ts) - A simple example that shows how to discover KNX devices on the network. | ||
* [test-toggle](./examples/test-toggle.ts) - An interactive example that shows how to toggle a switch on/off. | ||
* [sampleSecure](./examples/sampleSecure.ts) - A full featured example that shows how to connect to the KNX bus and send/receive telegrams in secure mode. | ||
// Set the properties | ||
let knxUltimateClientProperties = { | ||
ipAddr: "224.0.23.12", | ||
ipPort: "3671", | ||
physAddr: "1.1.100", | ||
suppress_ack_ldatareq: false, | ||
loglevel: "error", // or "debug" is the default | ||
localEchoInTunneling: true, // Leave true, forever. | ||
hostProtocol: "Multicast", // "Multicast" in case you use a KNX/IP Router, "TunnelUDP" in case of KNX/IP Interface, "TunnelTCP" in case of secure KNX/IP Interface (not yet implemented) | ||
isSecureKNXEnabled: false, // Leave "false" until KNX-Secure has been released | ||
jKNXSecureKeyring: "", // ETS Keyring JSON file (leave blank until KNX-Secure has been released) | ||
localIPAddress: "", // Leave blank, will be automatically filled by KNXUltimate | ||
}; | ||
// Instantiate the client | ||
const knxUltimateClient = new knx.KNXClient(knxUltimateClientProperties); | ||
// Setting handlers | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.indication, function (_datagram, _echoed) { | ||
// This function is called whenever a KNX telegram arrives from BUS | ||
// Get the event | ||
let _evt = ""; | ||
let dpt = ""; | ||
let jsValue; | ||
if (_datagram.cEMIMessage.npdu.isGroupRead) _evt = "GroupValue_Read"; | ||
if (_datagram.cEMIMessage.npdu.isGroupResponse) _evt = "GroupValue_Response"; | ||
if (_datagram.cEMIMessage.npdu.isGroupWrite) _evt = "GroupValue_Write"; | ||
// Get the source Address | ||
let _src = _datagram.cEMIMessage.srcAddress.toString(); | ||
// Get the destination GA | ||
let _dst = _datagram.cEMIMessage.dstAddress.toString() | ||
// Get the RAW Value | ||
let _Rawvalue = _datagram.cEMIMessage.npdu.dataValue; | ||
// Decode the telegram. | ||
if (_dst === "0/1/1") { | ||
// We know, for example, that 0/1/1 is a boolean DPT 1.001 | ||
dpt = dptlib.resolve("1.001"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
} else if (_dst === "0/1/2") { | ||
// We know , for example, that 0/1/2 is a DPT 232.600 Color RGB | ||
dpt = dptlib.resolve("232.600"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
} else { | ||
// All others... assume they are boolean | ||
dpt = dptlib.resolve("1.001"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
if (jsValue === null) { | ||
// Opppsss, it's null. It means that the datapoint isn't 1.001 | ||
// Raise whatever error you want. | ||
} | ||
} | ||
console.log("src: " + _src + " dest: " + _dst, " event: " + _evt, " value: " + jsValue); | ||
}); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.connected, info => { | ||
// The client is connected | ||
console.log("Connected. On Duty", info); | ||
// WARNING, THIS WILL WRITE ON YOUR KNX BUS! | ||
knxUltimateClient.write("0/1/1", false, "1.001"); | ||
}); | ||
// Connect | ||
try { | ||
knxUltimateClient.removeAllListeners(); | ||
} catch (error) { | ||
} | ||
knxUltimateClient.Connect(); | ||
``` | ||
<br/> | ||
<br/> | ||
## Full featured sample (you can find this sample in the "sample.js" file) | ||
```javascript | ||
const knx = require("./index.js"); | ||
const dptlib = require('./src/dptlib'); | ||
// Get a list of supported datapoints | ||
// With this function, you can see what datapoints are supported and a sample on how you need to format the payload to be sent. | ||
// ###################################### | ||
// Helpers | ||
sortBy = (field) => (a, b) => { | ||
if (a[field] > b[field]) { return 1 } else { return -1 } | ||
}; | ||
onlyDptKeys = (kv) => { | ||
return kv[0].startsWith("DPT") | ||
}; | ||
extractBaseNo = (kv) => { | ||
return { | ||
subtypes: kv[1].subtypes, | ||
base: parseInt(kv[1].id.replace("DPT", "")) | ||
} | ||
}; | ||
convertSubtype = (baseType) => (kv) => { | ||
let value = `${baseType.base}.${kv[0]}`; | ||
//let sRet = value + " " + kv[1].name + (kv[1].unit === undefined ? "" : " (" + kv[1].unit + ")"); | ||
let sRet = value + " " + kv[1].name; | ||
return { | ||
value: value | ||
, text: sRet | ||
} | ||
} | ||
toConcattedSubtypes = (acc, baseType) => { | ||
let subtypes = | ||
Object.entries(baseType.subtypes) | ||
.sort(sortBy(0)) | ||
.map(convertSubtype(baseType)) | ||
return acc.concat(subtypes) | ||
}; | ||
dptGetHelp = dpt => { | ||
var sDPT = dpt.split(".")[0]; // Takes only the main type | ||
var jRet; | ||
if (sDPT == "0") { // Special fake datapoint, meaning "Universal Mode" | ||
jRet = { | ||
"help": | ||
``, "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki" | ||
}; | ||
return (jRet); | ||
} | ||
jRet = { "help": "No sample currently avaiable", "helplink": "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome" }; | ||
const dpts = | ||
Object.entries(dptlib) | ||
.filter(onlyDptKeys) | ||
for (let index = 0; index < dpts.length; index++) { | ||
if (dpts[index][0].toUpperCase() === "DPT" + sDPT) { | ||
jRet = { "help": (dpts[index][1].basetype.hasOwnProperty("help") ? dpts[index][1].basetype.help : "No sample currently avaiable, just pass the payload as is it"), "helplink": (dpts[index][1].basetype.hasOwnProperty("helplink") ? dpts[index][1].basetype.helplink : "https://github.com/Supergiovane/node-red-contrib-knx-ultimate/wiki/-SamplesHome") }; | ||
break; | ||
} | ||
} | ||
return (jRet); | ||
} | ||
const dpts = | ||
Object.entries(dptlib) | ||
.filter(onlyDptKeys) | ||
.map(extractBaseNo) | ||
.sort(sortBy("base")) | ||
.reduce(toConcattedSubtypes, []) | ||
dpts.forEach(element => { | ||
console.log(element.text + " USAGE: " + dptGetHelp(element.value).help); | ||
console.log(" "); | ||
}); | ||
// ###################################### | ||
// Let's connect and turn on your appliance. | ||
// Set the properties | ||
let knxUltimateClientProperties = { | ||
ipAddr: "224.0.23.12", | ||
ipPort: "3671", | ||
physAddr: "1.1.100", | ||
suppress_ack_ldatareq: false, | ||
loglevel: "error", // or "debug" is the default | ||
localEchoInTunneling: true, // Leave true, forever. | ||
hostProtocol: "Multicast", // "Multicast" in case you use a KNX/IP Router, "TunnelUDP" in case of KNX/IP Interface, "TunnelTCP" in case of secure KNX/IP Interface (not yet implemented) | ||
isSecureKNXEnabled: false, // Leave "false" until KNX-Secure has been released | ||
jKNXSecureKeyring: "", // ETS Keyring JSON file (leave blank until KNX-Secure has been released) | ||
localIPAddress: "", // Leave blank, will be automatically filled by KNXUltimate | ||
}; | ||
var knxUltimateClient; | ||
// If you're reinstantiating a new knxUltimateClient object, you must remove all listeners. | ||
// If this is the first time you instantiate tne knxUltimateClient object, this part of code throws an error into the try...catch. | ||
try { | ||
if (knxUltimateClient !== null) knxUltimateClient.removeAllListeners(); | ||
} catch (error) { | ||
// New connection, do nothing. | ||
} | ||
// Let's go | ||
knxUltimateClient = new knx.KNXClient(knxUltimateClientProperties); | ||
// Setting handlers | ||
// ###################################### | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.indication, handleBusEvents); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.error, err => { | ||
// Error event | ||
console.log("Error", err) | ||
}); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.disconnected, info => { | ||
// The client is disconnected. Here you can handle the reconnection | ||
console.log("Disconnected", info) | ||
}); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.close, info => { | ||
// The client physical net socket has been closed | ||
console.log("Closed", info) | ||
}); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.ackReceived, (knxMessage, info) => { | ||
// In -->tunneling mode<-- (in ROUTING mode there is no ACK event), signals wether the last KNX telegram has been acknowledge or not | ||
// knxMessage: contains the telegram sent. | ||
// info is true it the last telegram has been acknowledge, otherwise false. | ||
console.log("Last telegram acknowledge", knxMessage, info) | ||
}); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.connected, info => { | ||
// The client is connected | ||
console.log("Connected. On Duty", info) | ||
// Check wether knxUltimateClient is clear to send the next telegram. | ||
// This should be called bevore any .write, .response, and .read request. | ||
// If not clear to send, retry later because the knxUltimateClient is busy in sending another telegram. | ||
console.log("Clear to send: " + knxUltimateClient._getClearToSend()) | ||
// // Send a WRITE telegram to the KNX BUS | ||
// // You need: group address, payload (true/false/or any message), datapoint as string | ||
let payload = false; | ||
if (knxUltimateClient._getClearToSend()) knxUltimateClient.write("0/1/1", payload, "1.001"); | ||
// Send a color RED to an RGB datapoint | ||
payload = { red: 125, green: 0, blue: 0 }; | ||
if (knxUltimateClient._getClearToSend()) knxUltimateClient.write("0/1/2", payload, "232.600"); | ||
// // Send a READ request to the KNX BUS | ||
if (knxUltimateClient._getClearToSend()) knxUltimateClient.read("0/0/1"); | ||
// Send a RESPONSE telegram to the KNX BUS | ||
// You need: group address, payload (true/false/or any message), datapoint as string | ||
payload = false; | ||
if (knxUltimateClient._getClearToSend()) knxUltimateClient.respond("0/0/1", payload, "1.001"); | ||
}); | ||
knxUltimateClient.on(knx.KNXClient.KNXClientEvents.connecting, info => { | ||
// The client is setting up the connection | ||
console.log("Connecting...", info) | ||
}); | ||
// ###################################### | ||
knxUltimateClient.Connect(); | ||
// Handle BUS events | ||
// --------------------------------------------------------------------------------------- | ||
function handleBusEvents(_datagram, _echoed) { | ||
// This function is called whenever a KNX telegram arrives from BUS | ||
// Get the event | ||
let _evt = ""; | ||
let dpt = ""; | ||
let jsValue; | ||
if (_datagram.cEMIMessage.npdu.isGroupRead) _evt = "GroupValue_Read"; | ||
if (_datagram.cEMIMessage.npdu.isGroupResponse) _evt = "GroupValue_Response"; | ||
if (_datagram.cEMIMessage.npdu.isGroupWrite) _evt = "GroupValue_Write"; | ||
// Get the source Address | ||
let _src = _datagram.cEMIMessage.srcAddress.toString(); | ||
// Get the destination GA | ||
let _dst = _datagram.cEMIMessage.dstAddress.toString() | ||
// Get the RAW Value | ||
let _Rawvalue = _datagram.cEMIMessage.npdu.dataValue; | ||
// Decode the telegram. | ||
if (_dst === "0/1/1") { | ||
// We know that 0/1/1 is a boolean DPT 1.001 | ||
dpt = dptlib.resolve("1.001"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
} else if (_dst === "0/1/2") { | ||
// We know that 0/1/2 is a boolean DPT 232.600 Color RGB | ||
dpt = dptlib.resolve("232.600"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
} else { | ||
// All others... assume they are boolean | ||
dpt = dptlib.resolve("1.001"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
if (jsValue === null) { | ||
// Is null, try if it's a numerical value | ||
dpt = dptlib.resolve("5.001"); | ||
jsValue = dptlib.fromBuffer(_Rawvalue, dpt) | ||
} | ||
} | ||
console.log("src: " + _src + " dest: " + _dst, " event: " + _evt, " value: " + jsValue); | ||
} | ||
// Disconnect after 20 secs. | ||
setTimeout(() => { | ||
if (knxUltimateClient.isConnected()) knxUltimateClient.Disconnect(); | ||
}, 20000); | ||
``` | ||
<br/> | ||
<br/> | ||
## SUGGESTION | ||
@@ -407,0 +101,0 @@ > |
@@ -169,4 +169,2 @@ // Made with love by Supergiovane | ||
private _awaitingResponsePromise: Promise<void> | ||
private _clientSocket: UDPSocket | TCPSocket | ||
@@ -178,6 +176,2 @@ | ||
private_heartbeatRunning: boolean | ||
private_clearToSend: boolean | ||
private _clearToSend: boolean | ||
@@ -223,4 +217,4 @@ | ||
// add an error listener otherwise without this | ||
// the emit error would throw | ||
// add an empty error listener, without this | ||
// every "error" emitted would throw an unhandled exception | ||
this.on('error', (error) => {}) | ||
@@ -243,4 +237,2 @@ | ||
this.removeAllListeners() | ||
if (this._options.hostProtocol === 'TunnelUDP') { | ||
@@ -251,3 +243,3 @@ this._clientSocket = dgram.createSocket({ | ||
}) as UDPSocket | ||
this._clientSocket.removeAllListeners() | ||
// this._clientSocket.removeAllListeners() | ||
this._clientSocket.bind( | ||
@@ -272,3 +264,3 @@ { port: null, address: this._options.localIPAddress }, | ||
SocketEvents.message, | ||
this._processInboundMessage.bind(this), | ||
this.processInboundMessage.bind(this), | ||
) | ||
@@ -283,3 +275,3 @@ this._clientSocket.on(SocketEvents.error, (error) => | ||
this._clientSocket = new net.Socket() | ||
this._clientSocket.removeAllListeners() | ||
// this._clientSocket.removeAllListeners() | ||
this._clientSocket.on(SocketEvents.data, (data) => { | ||
@@ -299,7 +291,7 @@ console.log('Received message', data) | ||
}) as UDPSocket | ||
this._clientSocket.removeAllListeners() | ||
// this._clientSocket.removeAllListeners() | ||
this._clientSocket.on(SocketEvents.listening, () => {}) | ||
this._clientSocket.on( | ||
SocketEvents.message, | ||
this._processInboundMessage.bind(this), | ||
this.processInboundMessage.bind(this), | ||
) | ||
@@ -339,2 +331,5 @@ this._clientSocket.on(SocketEvents.error, (error) => | ||
/** | ||
* The channel ID of the connection. Only defined after a successful connection | ||
*/ | ||
get channelID() { | ||
@@ -344,7 +339,18 @@ return this._channelID | ||
getKNXDataBuffer(_data: Buffer, _dptid: string) { | ||
/** | ||
* Handle the busy state, for example while waiting for ACK. When true means we can send new telegrams to bus | ||
*/ | ||
get clearToSend(): boolean { | ||
return this._clearToSend !== undefined ? this._clearToSend : true | ||
} | ||
private getKNXDataBuffer(data: Buffer, dptid: string | number) { | ||
if (typeof dptid === 'number') { | ||
dptid = dptid.toString() | ||
} | ||
const adpu = {} as DPTLib.APDU | ||
DPTLib.populateAPDU(_data, adpu, _dptid) | ||
DPTLib.populateAPDU(data, adpu, dptid) | ||
const iDatapointType: number = parseInt( | ||
_dptid.substr(0, _dptid.indexOf('.')), | ||
dptid.substring(0, dptid.indexOf('.')), | ||
) | ||
@@ -373,8 +379,12 @@ const isSixBits: boolean = adpu.bitlength <= 6 | ||
private async waitForEvent(event: KNXClientEvents, timeout: number) { | ||
let resolveRef: () => void | ||
return Promise.race<void>([ | ||
new Promise((resolve) => { | ||
resolveRef = resolve | ||
this.once(event, resolve) | ||
}), | ||
wait(timeout), | ||
]) | ||
]).then(() => { | ||
this.off(event, resolveRef) | ||
}) | ||
} | ||
@@ -417,2 +427,5 @@ | ||
/** | ||
* Write knxPacket to socket | ||
*/ | ||
send(knxPacket: KNXPacket): void { | ||
@@ -509,4 +522,12 @@ if (knxPacket instanceof KNXConnectRequest) { | ||
// sendWriteRequest(dstAddress, data) { | ||
write(dstAddress: KNXAddress | string, data: any, dptid: any): void { | ||
/** Sends a WRITE telegram to the BUS. | ||
* `dstAddress` is the group address (for example "0/0/1"), | ||
* `data` is the value you want to send (for example true), | ||
* `dptid` is a string/number representing the datapoint (for example "5.001") | ||
*/ | ||
write( | ||
dstAddress: KNXAddress | string, | ||
data: any, | ||
dptid: string | number, | ||
): void { | ||
if (this._connectionState !== ConncetionState.CONNECTED) | ||
@@ -558,3 +579,3 @@ throw new Error( | ||
cEMIMessage.control.hopCount = 6 | ||
const seqNum: number = this._incSeqNumber() // 26/12/2021 | ||
const seqNum: number = this.incSeqNumber() // 26/12/2021 | ||
const knxPacketRequest = KNXProtocol.newKNXTunnelingRequest( | ||
@@ -566,3 +587,3 @@ this._channelID, | ||
if (!this._options.suppress_ack_ldatareq) | ||
this._setTimerWaitingForACK(knxPacketRequest) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
@@ -575,4 +596,13 @@ // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
// sendResponseRequest | ||
respond(dstAddress: KNXAddress | string, data: Buffer, dptid: any): void { | ||
/** | ||
* Sends a RESPONSE telegram to the BUS. | ||
* `dstAddress` is the group address (for example "0/0/1"), | ||
* `data` is the value you want to send (for example true), | ||
* `dptid` is a string/number representing the datapoint (for example "5.001") | ||
*/ | ||
respond( | ||
dstAddress: KNXAddress | string, | ||
data: Buffer, | ||
dptid: string | number, | ||
): void { | ||
if (this._connectionState !== ConncetionState.CONNECTED) | ||
@@ -624,3 +654,3 @@ throw new Error( | ||
cEMIMessage.control.hopCount = 6 | ||
const seqNum: number = this._incSeqNumber() // 26/12/2021 | ||
const seqNum: number = this.incSeqNumber() // 26/12/2021 | ||
const knxPacketRequest = KNXProtocol.newKNXTunnelingRequest( | ||
@@ -632,3 +662,3 @@ this._channelID, | ||
if (!this._options.suppress_ack_ldatareq) | ||
this._setTimerWaitingForACK(knxPacketRequest) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
@@ -641,3 +671,5 @@ // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
// sendReadRequest | ||
/** | ||
* Sends a READ telegram to the BUS. GA is the group address (for example "0/0/1"). | ||
*/ | ||
read(dstAddress: KNXAddress | string): void { | ||
@@ -687,3 +719,3 @@ if (this._connectionState !== ConncetionState.CONNECTED) | ||
cEMIMessage.control.hopCount = 6 | ||
const seqNum: number = this._incSeqNumber() // 26/12/2021 | ||
const seqNum: number = this.incSeqNumber() // 26/12/2021 | ||
const knxPacketRequest = KNXProtocol.newKNXTunnelingRequest( | ||
@@ -695,3 +727,3 @@ this._channelID, | ||
if (!this._options.suppress_ack_ldatareq) | ||
this._setTimerWaitingForACK(knxPacketRequest) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
@@ -704,5 +736,11 @@ // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
/** | ||
* Sends a WRITE telegram to the BUS. | ||
* `dstAddress` is the group address (for example "0/0/1"), | ||
* `rawDataBuffer` is the buffer you want to send, | ||
* `dptid` is a string/number representing the datapoint (for example "5.001") | ||
*/ | ||
writeRaw( | ||
dstAddress: KNXAddress | string, | ||
_rawDataBuffer: Buffer, | ||
rawDataBuffer: Buffer, | ||
bitlength: number, | ||
@@ -717,3 +755,3 @@ ): void { | ||
if (!Buffer.isBuffer(_rawDataBuffer)) { | ||
if (!Buffer.isBuffer(rawDataBuffer)) { | ||
this.sysLogger.error( | ||
@@ -736,3 +774,3 @@ 'KNXClient: writeRaw: Value must be a buffer! ', | ||
const baseBufferFromBitLenght: Buffer = Buffer.alloc(bitlength / 8) // The buffer lenght must be like specified by bitlenght | ||
_rawDataBuffer.copy(baseBufferFromBitLenght, 0) | ||
rawDataBuffer.copy(baseBufferFromBitLenght, 0) | ||
const data: KNXDataBuffer = new KNXDataBuffer( | ||
@@ -781,3 +819,3 @@ baseBufferFromBitLenght, | ||
cEMIMessage.control.hopCount = 6 | ||
const seqNum: number = this._incSeqNumber() // 26/12/2021 | ||
const seqNum: number = this.incSeqNumber() // 26/12/2021 | ||
const knxPacketRequest = KNXProtocol.newKNXTunnelingRequest( | ||
@@ -789,3 +827,3 @@ this._channelID, | ||
if (!this._options.suppress_ack_ldatareq) | ||
this._setTimerWaitingForACK(knxPacketRequest) | ||
this.setTimerWaitingForACK(knxPacketRequest) | ||
this.send(knxPacketRequest) | ||
@@ -798,10 +836,10 @@ // 06/12/2021 Echo the sent telegram. Last parameter is the echo true/false | ||
startHeartBeat(): void { | ||
private startHeartBeat(): void { | ||
this.stopHeartBeat() | ||
this._heartbeatFailures = 0 | ||
this._heartbeatRunning = true | ||
this._runHeartbeat() | ||
this.runHeartbeat() | ||
} | ||
stopHeartBeat(): void { | ||
private stopHeartBeat(): void { | ||
this._heartbeatRunning = false | ||
@@ -812,2 +850,5 @@ this.clearTimer(KNXTimer.HEARTBEAT) | ||
/** | ||
* Returns true if discovery is running | ||
*/ | ||
isDiscoveryRunning() { | ||
@@ -817,2 +858,5 @@ return this.timers.has(KNXTimer.DISCOVERY) | ||
/** | ||
* Send a search request message to the KNX bus and wait for responses | ||
*/ | ||
startDiscovery() { | ||
@@ -827,5 +871,8 @@ if (this.isDiscoveryRunning()) { | ||
) | ||
this._sendSearchRequestMessage() | ||
this.sendSearchRequestMessage() | ||
} | ||
/** | ||
* Stop the discovery process | ||
*/ | ||
stopDiscovery() { | ||
@@ -835,4 +882,5 @@ this.clearTimer(KNXTimer.DISCOVERY) | ||
/** Returns an array of discovered KNX interfaces in the format "<ip>:<port>" */ | ||
public static async discover(timeout?: number): Promise<string[]> | ||
/** | ||
* Returns an array of discovered KNX interfaces in the format "<ip>:<port>" | ||
*/ | ||
public static async discover(eth?: string | number, timeout = 5000) { | ||
@@ -873,2 +921,6 @@ if (typeof eth === 'number') { | ||
// } | ||
/** | ||
* Connect to the KNX bus | ||
*/ | ||
Connect(knxLayer = TunnelTypes.TUNNEL_LINKLAYER) { | ||
@@ -919,3 +971,3 @@ if (this._clientSocket == null) { | ||
() => { | ||
this._sendConnectRequestMessage(new TunnelCRI(knxLayer)) | ||
this.sendConnectRequestMessage(new TunnelCRI(knxLayer)) | ||
}, | ||
@@ -937,3 +989,3 @@ 2000, | ||
if (this._options.isSecureKNXEnabled) | ||
this._sendSecureSessionRequestMessage( | ||
this.sendSecureSessionRequestMessage( | ||
new TunnelCRI(knxLayer), | ||
@@ -955,2 +1007,5 @@ ) | ||
/** | ||
* Close the socket connection | ||
*/ | ||
private async closeSocket() { | ||
@@ -976,2 +1031,5 @@ return new Promise<void>((resolve) => { | ||
/** | ||
* Sends a DISCONNECT_REQUEST telegram to the BUS and closes the socket | ||
*/ | ||
async Disconnect() { | ||
@@ -1002,3 +1060,3 @@ if (this._clientSocket === null) { | ||
this._awaitingResponseType = KNX_CONSTANTS.DISCONNECT_RESPONSE | ||
this._sendDisconnectRequestMessage(this._channelID) | ||
this.sendDisconnectRequestMessage(this._channelID) | ||
@@ -1010,3 +1068,3 @@ // wait for disconnect event or at most 2 seconds | ||
if (this._connectionState !== ConncetionState.DISCONNECTED) { | ||
this._setDisconnected( | ||
this.setDisconnected( | ||
"Forced call from KNXClient Disconnect() function, because the KNX Interface hasn't sent the DISCONNECT_RESPONSE in time.", | ||
@@ -1017,2 +1075,5 @@ ) | ||
/** | ||
* Returns true if the socket is connected | ||
*/ | ||
isConnected() { | ||
@@ -1022,3 +1083,6 @@ return this._connectionState === ConncetionState.CONNECTED | ||
async _setDisconnected(_sReason = '') { | ||
/** | ||
* Close the socket connection without sending a disconnect request | ||
*/ | ||
private async setDisconnected(_sReason = '') { | ||
this.sysLogger.debug( | ||
@@ -1044,3 +1108,6 @@ `KNXClient: called _setDisconnected ${this._options.ipAddr}:${this._options.ipPort} ${_sReason}`, | ||
_runHeartbeat() { | ||
/** | ||
* Send a connection state request message to the KNX bus and schedule the next heartbeat | ||
*/ | ||
private runHeartbeat() { | ||
if (!this._heartbeatRunning) { | ||
@@ -1075,3 +1142,3 @@ return | ||
this.emit(KNXClientEvents.error, deadError) | ||
this._setDisconnected(deadError.message) | ||
this.setDisconnected(deadError.message) | ||
} | ||
@@ -1082,3 +1149,3 @@ }, | ||
this._awaitingResponseType = KNX_CONSTANTS.CONNECTIONSTATE_RESPONSE | ||
this._sendConnectionStateRequestMessage(this._channelID) | ||
this.sendConnectionStateRequestMessage(this._channelID) | ||
@@ -1089,3 +1156,3 @@ // schedule next heartbeat | ||
() => { | ||
this._runHeartbeat() | ||
this.runHeartbeat() | ||
}, | ||
@@ -1096,12 +1163,13 @@ 1000 * this._options.connectionKeepAliveTimeout, | ||
_getSeqNumber() { | ||
/** | ||
* Get actual tunneling sequence number | ||
*/ | ||
private getSeqNumber() { | ||
return this._clientTunnelSeqNumber | ||
} | ||
/** 26/12/2021 Handle the busy state, for example while waiting for ACK */ | ||
_getClearToSend() { | ||
return this._clearToSend !== undefined ? this._clearToSend : true | ||
} | ||
_incSeqNumber() { | ||
/** | ||
* Increment the tunneling sequence number | ||
*/ | ||
private incSeqNumber() { | ||
this._clientTunnelSeqNumber++ | ||
@@ -1117,3 +1185,6 @@ if (this._clientTunnelSeqNumber > 255) { | ||
// } | ||
_setTimerWaitingForACK(knxTunnelingRequest: KNXTunnelingRequest) { | ||
/** | ||
* Setup a timer while waiting for an ACK of `knxTunnelingRequest` | ||
*/ | ||
private setTimerWaitingForACK(knxTunnelingRequest: KNXTunnelingRequest) { | ||
this._clearToSend = false // 26/12/2021 stop sending until ACK received | ||
@@ -1147,7 +1218,7 @@ const timeoutErr = new errors.RequestTimeoutError( | ||
timeoutErr.message || 'Undef error' | ||
} no ACK received. ABORT sending datagram with seqNumber ${this._getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`, | ||
} no ACK received. ABORT sending datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`, | ||
) | ||
} else { | ||
// 26/12/2021 // If no ACK received, resend the datagram once with the same sequence number | ||
this._setTimerWaitingForACK(knxTunnelingRequest) | ||
this.setTimerWaitingForACK(knxTunnelingRequest) | ||
this.send(knxTunnelingRequest) | ||
@@ -1157,3 +1228,3 @@ this.sysLogger.error( | ||
timeoutErr.message || 'Undef error' | ||
} no ACK received. Retransmit datagram with seqNumber ${this._getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`, | ||
} no ACK received. Retransmit datagram with seqNumber ${this.getSeqNumber()} from ${knxTunnelingRequest.cEMIMessage.srcAddress.toString()} to ${knxTunnelingRequest.cEMIMessage.dstAddress.toString()}`, | ||
) | ||
@@ -1166,3 +1237,6 @@ } | ||
_processInboundMessage(msg: Buffer, rinfo: RemoteInfo) { | ||
/** | ||
* Process a raw message coming from the socket | ||
*/ | ||
private processInboundMessage(msg: Buffer, rinfo: RemoteInfo) { | ||
let sProcessInboundLog = '' | ||
@@ -1227,3 +1301,3 @@ | ||
) | ||
this._setDisconnected( | ||
this.setDisconnected( | ||
`Connect response error ${knxConnectResponse.status}`, | ||
@@ -1262,3 +1336,3 @@ ) | ||
} | ||
this._setDisconnected( | ||
this.setDisconnected( | ||
'Received DISCONNECT_RESPONSE from the KNX interface.', | ||
@@ -1279,3 +1353,3 @@ ) | ||
this._connectionState = ConncetionState.DISCONNECTING | ||
this._sendDisconnectResponseMessage( | ||
this.sendDisconnectResponseMessage( | ||
knxDisconnectRequest.channelID, | ||
@@ -1288,3 +1362,3 @@ ) | ||
() => { | ||
this._setDisconnected( | ||
this.setDisconnected( | ||
`Received KNX packet: DISCONNECT_REQUEST, ChannelID:${this._channelID} Host:${this._options.ipAddr}:${this._options.ipPort}`, | ||
@@ -1365,3 +1439,3 @@ ) | ||
if (!this._options.suppress_ack_ldatareq) { | ||
if (knxTunnelingAck.seqCounter === this._getSeqNumber()) { | ||
if (knxTunnelingAck.seqCounter === this.getSeqNumber()) { | ||
this.clearTimer(KNXTimer.ACK) | ||
@@ -1443,3 +1517,3 @@ this._numFailedTelegramACK = 0 // 25/12/2021 clear the current ACK failed telegram number | ||
) | ||
this._setDisconnected( | ||
this.setDisconnected( | ||
`Awaiting response ${this._awaitingResponseType}, received connection state response with status ${knxConnectionStateResponse.status}`, | ||
@@ -1474,3 +1548,3 @@ ) | ||
_sendDescriptionRequestMessage() { | ||
private sendDescriptionRequestMessage() { | ||
this.send( | ||
@@ -1483,3 +1557,3 @@ KNXProtocol.newKNXDescriptionRequest( | ||
_sendSearchRequestMessage() { | ||
private sendSearchRequestMessage() { | ||
this.send( | ||
@@ -1494,3 +1568,3 @@ KNXProtocol.newKNXSearchRequest( | ||
_sendConnectRequestMessage(cri: TunnelCRI) { | ||
private sendConnectRequestMessage(cri: TunnelCRI) { | ||
// try { | ||
@@ -1505,3 +1579,3 @@ // const oHPAI = new HPAI(this._options.localSocketAddress.address, this._options.localSocketAddress.port, this._options.hostProtocol === 'TunnelTCP' ? KNX_CONSTANTS.IPV4_TCP : KNX_CONSTANTS.IPV4_UDP) | ||
_sendConnectionStateRequestMessage(channelID: number) { | ||
private sendConnectionStateRequestMessage(channelID: number) { | ||
// try { | ||
@@ -1516,3 +1590,3 @@ // const oHPAI = new HPAI.HPAI(this._options.localSocketAddress.address, this._options.localSocketAddress.port, this._options.hostProtocol === 'TunnelTCP' ? KNX_CONSTANTS.IPV4_TCP : KNX_CONSTANTS.IPV4_UDP) | ||
_sendDisconnectRequestMessage(channelID: number) { | ||
private sendDisconnectRequestMessage(channelID: number) { | ||
// try { | ||
@@ -1527,3 +1601,3 @@ // const oHPAI = new HPAI.HPAI(this._options.localSocketAddress.address, this._options.localSocketAddress.port, this._options.hostProtocol === 'TunnelTCP' ? KNX_CONSTANTS.IPV4_TCP : KNX_CONSTANTS.IPV4_UDP) | ||
_sendDisconnectResponseMessage( | ||
private sendDisconnectResponseMessage( | ||
channelID: number, | ||
@@ -1535,3 +1609,3 @@ status = ConnectionStatus.E_NO_ERROR, | ||
_sendSecureSessionRequestMessage(cri: TunnelCRI) { | ||
private sendSecureSessionRequestMessage(cri: TunnelCRI) { | ||
const oHPAI = new HPAI( | ||
@@ -1538,0 +1612,0 @@ '0.0.0.0', |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
20774
2
974604
112