knx.ts 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +51 -21
- package/README.md +274 -61
- package/dist/@types/interfaces/connection.d.ts +80 -13
- package/dist/@types/interfaces/servers.d.ts +18 -0
- package/dist/@types/interfaces/servers.js +2 -0
- package/dist/connection/KNXService.d.ts +13 -30
- package/dist/connection/KNXService.js +4 -164
- package/dist/connection/KNXTunneling.d.ts +4 -4
- package/dist/connection/KNXTunneling.js +35 -62
- package/dist/connection/KNXUSBConnection.d.ts +20 -0
- package/dist/connection/KNXUSBConnection.js +358 -0
- package/dist/connection/KNXnetIPServer.d.ts +29 -12
- package/dist/connection/KNXnetIPServer.js +261 -83
- package/dist/connection/Router.d.ts +52 -32
- package/dist/connection/Router.js +225 -153
- package/dist/connection/TPUART.d.ts +8 -3
- package/dist/connection/TPUART.js +41 -37
- package/dist/connection/TunnelConnection.d.ts +3 -1
- package/dist/connection/TunnelConnection.js +6 -4
- package/dist/core/CEMI.d.ts +7 -2
- package/dist/core/CEMI.js +5 -8
- package/dist/core/EMI.d.ts +312 -200
- package/dist/core/EMI.js +511 -1007
- package/dist/core/KNXnetIPStructures.d.ts +10 -1
- package/dist/core/KNXnetIPStructures.js +15 -10
- package/dist/core/MessageCodeField.d.ts +1 -1
- package/dist/core/cache/GroupAddressCache.d.ts +57 -0
- package/dist/core/cache/GroupAddressCache.js +227 -0
- package/dist/core/data/KNXDataDecode.d.ts +2 -2
- package/dist/core/data/KNXDataDecode.js +198 -183
- package/dist/core/enum/EnumControlField.d.ts +0 -5
- package/dist/core/enum/EnumControlField.js +1 -7
- package/dist/core/enum/EnumControlFieldExtended.d.ts +1 -1
- package/dist/core/enum/EnumShortACKFrame.d.ts +1 -1
- package/dist/core/enum/ErrorCodeSet.js +59 -0
- package/dist/core/enum/KNXnetIPEnum.d.ts +2 -2
- package/dist/core/enum/KNXnetIPEnum.js +19 -1
- package/dist/core/layers/data/NPDU.d.ts +2 -1
- package/dist/core/layers/data/NPDU.js +6 -3
- package/dist/index.d.ts +19 -2
- package/dist/index.js +36 -1
- package/dist/server/KNXMQTTGateway.d.ts +13 -0
- package/dist/server/KNXMQTTGateway.js +164 -0
- package/dist/server/KNXWebSocketServer.d.ts +12 -0
- package/dist/server/KNXWebSocketServer.js +118 -0
- package/dist/utils/CEMIAdapter.d.ts +4 -3
- package/dist/utils/CEMIAdapter.js +26 -30
- package/dist/utils/Logger.d.ts +4 -4
- package/dist/utils/Logger.js +3 -7
- package/package.json +27 -7
|
@@ -17,8 +17,3 @@ export declare enum FrameType {
|
|
|
17
17
|
EXTENDED = 0,// FT = 0 → L_Data_Extended Frame
|
|
18
18
|
STANDARD = 1
|
|
19
19
|
}
|
|
20
|
-
export declare enum FrameKind {
|
|
21
|
-
L_DATA_FRAME = "L_DATA_FRAME",
|
|
22
|
-
L_POLL_DATA_FRAME = "L_POLL_DATA_FRAME",
|
|
23
|
-
ACKNOWLEDGEMENT_FRAME = "ACKNOWLEDGEMENT_FRAME"
|
|
24
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.FrameType = exports.Priority = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* The two priority bits of the Control field shall control the priority of the Frame, if two devices start
|
|
6
6
|
transmission simultaneously.
|
|
@@ -28,9 +28,3 @@ var FrameType;
|
|
|
28
28
|
FrameType[FrameType["EXTENDED"] = 0] = "EXTENDED";
|
|
29
29
|
FrameType[FrameType["STANDARD"] = 1] = "STANDARD"; // FT = 1 → L_Data_Standard Frame
|
|
30
30
|
})(FrameType || (exports.FrameType = FrameType = {}));
|
|
31
|
-
var FrameKind;
|
|
32
|
-
(function (FrameKind) {
|
|
33
|
-
FrameKind["L_DATA_FRAME"] = "L_DATA_FRAME";
|
|
34
|
-
FrameKind["L_POLL_DATA_FRAME"] = "L_POLL_DATA_FRAME";
|
|
35
|
-
FrameKind["ACKNOWLEDGEMENT_FRAME"] = "ACKNOWLEDGEMENT_FRAME";
|
|
36
|
-
})(FrameKind || (exports.FrameKind = FrameKind = {}));
|
|
@@ -5,7 +5,7 @@ export declare enum AddressType {
|
|
|
5
5
|
GROUP = 1
|
|
6
6
|
}
|
|
7
7
|
/** Extended Frame Format (bits 3..0) con 16 posibles valores (0..15) */
|
|
8
|
-
export declare enum ExtendedFrameFormat {
|
|
8
|
+
export declare const enum ExtendedFrameFormat {
|
|
9
9
|
/**
|
|
10
10
|
* If the {@linkcode AddressType} is Individual (0) then it will be **Point_To_Point**, if it is Group (1) then it will be **Standard Group**
|
|
11
11
|
*/
|
|
@@ -1,2 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorCodeSet = void 0;
|
|
4
|
+
var ErrorCodeSet;
|
|
5
|
+
(function (ErrorCodeSet) {
|
|
6
|
+
/**
|
|
7
|
+
* unknown error
|
|
8
|
+
* @service R/W
|
|
9
|
+
*/
|
|
10
|
+
ErrorCodeSet[ErrorCodeSet["Unspecified_Error"] = 0] = "Unspecified_Error";
|
|
11
|
+
/**
|
|
12
|
+
* write value not allowed (general, if not error 2 or 3)
|
|
13
|
+
* @service W
|
|
14
|
+
*/
|
|
15
|
+
ErrorCodeSet[ErrorCodeSet["Out_of_Range"] = 1] = "Out_of_Range";
|
|
16
|
+
/**
|
|
17
|
+
* write value to high
|
|
18
|
+
* @service W
|
|
19
|
+
*/
|
|
20
|
+
ErrorCodeSet[ErrorCodeSet["Out_of_MaxRange"] = 2] = "Out_of_MaxRange";
|
|
21
|
+
/**
|
|
22
|
+
* write value to low
|
|
23
|
+
* @service W
|
|
24
|
+
*/
|
|
25
|
+
ErrorCodeSet[ErrorCodeSet["Out_of_MinRange"] = 3] = "Out_of_MinRange";
|
|
26
|
+
/**
|
|
27
|
+
* memory can not be written or only with fault(s)
|
|
28
|
+
* @service W
|
|
29
|
+
*/
|
|
30
|
+
ErrorCodeSet[ErrorCodeSet["Memory_Error"] = 4] = "Memory_Error";
|
|
31
|
+
/**
|
|
32
|
+
* write access to a ‘read only’ or a write protected Property
|
|
33
|
+
* @service W
|
|
34
|
+
*/
|
|
35
|
+
ErrorCodeSet[ErrorCodeSet["Read_Only"] = 5] = "Read_Only";
|
|
36
|
+
/**
|
|
37
|
+
* COMMAND not valid or not supported
|
|
38
|
+
* @service W
|
|
39
|
+
*/
|
|
40
|
+
ErrorCodeSet[ErrorCodeSet["Illegal_COMMAND"] = 6] = "Illegal_COMMAND";
|
|
41
|
+
/**
|
|
42
|
+
* read or write access to an non existing Property
|
|
43
|
+
* @service R/W
|
|
44
|
+
*/
|
|
45
|
+
ErrorCodeSet[ErrorCodeSet["Void_DP"] = 7] = "Void_DP";
|
|
46
|
+
/**
|
|
47
|
+
* write access with a wrong data type (Datapoint length)
|
|
48
|
+
* @service W
|
|
49
|
+
*/
|
|
50
|
+
ErrorCodeSet[ErrorCodeSet["Type_Conflict"] = 8] = "Type_Conflict";
|
|
51
|
+
/**
|
|
52
|
+
* read or write access to a non existing Property array index
|
|
53
|
+
* @service R/W
|
|
54
|
+
*/
|
|
55
|
+
ErrorCodeSet[ErrorCodeSet["Prop_Index_Range_Error"] = 9] = "Prop_Index_Range_Error";
|
|
56
|
+
/**
|
|
57
|
+
* The Property exists but can at this moment not be written with a new value
|
|
58
|
+
* @service W
|
|
59
|
+
*/
|
|
60
|
+
ErrorCodeSet[ErrorCodeSet["Value_temporarily_not_writeable"] = 10] = "Value_temporarily_not_writeable";
|
|
61
|
+
})(ErrorCodeSet || (exports.ErrorCodeSet = ErrorCodeSet = {}));
|
|
@@ -81,14 +81,14 @@ export declare const enum AllowedSupportedServiceFamilies {
|
|
|
81
81
|
ObjectServer = 8,
|
|
82
82
|
Security = 9
|
|
83
83
|
}
|
|
84
|
-
export declare enum KNXTimeoutConstants {
|
|
84
|
+
export declare const enum KNXTimeoutConstants {
|
|
85
85
|
CONNECT_REQUEST_TIMEOUT = 10,
|
|
86
86
|
CONNECTIONSTATE_REQUEST_TIMEOUT = 10,
|
|
87
87
|
DEVICE_CONFIGURATION_REQUEST_TIMEOUT = 10,
|
|
88
88
|
TUNNELING_REQUEST_TIMEOUT = 1,
|
|
89
89
|
CONNECTION_ALIVE_TIME = 120
|
|
90
90
|
}
|
|
91
|
-
export declare enum TunnelLink {
|
|
91
|
+
export declare const enum TunnelLink {
|
|
92
92
|
TUNNEL_LINKLAYER = 2,
|
|
93
93
|
TUNNEL_RAW = 4,
|
|
94
94
|
TUNNEL_BUSMONITOR = 128
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TunnelLink = exports.KNXTimeoutConstants = exports.KNXLayer = exports.DescriptionType = exports.ConnectionType = exports.HostProtocolCode = exports.KNXnetIPErrorCodes = exports.KNXnetIPServiceType = void 0;
|
|
3
|
+
exports.TunnelLink = exports.KNXTimeoutConstants = exports.AllowedSupportedServiceFamilies = exports.KNXMedium = exports.KNXLayer = exports.DescriptionType = exports.ConnectionType = exports.HostProtocolCode = exports.KNXnetIPErrorCodes = exports.KNXnetIPServiceType = void 0;
|
|
4
4
|
var KNXnetIPServiceType;
|
|
5
5
|
(function (KNXnetIPServiceType) {
|
|
6
6
|
KNXnetIPServiceType[KNXnetIPServiceType["SEARCH_REQUEST"] = 513] = "SEARCH_REQUEST";
|
|
@@ -74,6 +74,24 @@ var KNXLayer;
|
|
|
74
74
|
KNXLayer[KNXLayer["RAW_LAYER"] = 4] = "RAW_LAYER";
|
|
75
75
|
KNXLayer[KNXLayer["BUSMONITOR_LAYER"] = 128] = "BUSMONITOR_LAYER";
|
|
76
76
|
})(KNXLayer || (exports.KNXLayer = KNXLayer = {}));
|
|
77
|
+
var KNXMedium;
|
|
78
|
+
(function (KNXMedium) {
|
|
79
|
+
KNXMedium[KNXMedium["TP1"] = 2] = "TP1";
|
|
80
|
+
KNXMedium[KNXMedium["PL110"] = 4] = "PL110";
|
|
81
|
+
KNXMedium[KNXMedium["RF"] = 16] = "RF";
|
|
82
|
+
KNXMedium[KNXMedium["KNXIP"] = 32] = "KNXIP";
|
|
83
|
+
})(KNXMedium || (exports.KNXMedium = KNXMedium = {}));
|
|
84
|
+
var AllowedSupportedServiceFamilies;
|
|
85
|
+
(function (AllowedSupportedServiceFamilies) {
|
|
86
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["Core"] = 2] = "Core";
|
|
87
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["DeviceManagement"] = 3] = "DeviceManagement";
|
|
88
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["Tunnelling"] = 4] = "Tunnelling";
|
|
89
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["Routing"] = 5] = "Routing";
|
|
90
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["RemoteLogging"] = 6] = "RemoteLogging";
|
|
91
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["RemoteConfigurationAndDiagnosis"] = 7] = "RemoteConfigurationAndDiagnosis";
|
|
92
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["ObjectServer"] = 8] = "ObjectServer";
|
|
93
|
+
AllowedSupportedServiceFamilies[AllowedSupportedServiceFamilies["Security"] = 9] = "Security";
|
|
94
|
+
})(AllowedSupportedServiceFamilies || (exports.AllowedSupportedServiceFamilies = AllowedSupportedServiceFamilies = {}));
|
|
77
95
|
var KNXTimeoutConstants;
|
|
78
96
|
(function (KNXTimeoutConstants) {
|
|
79
97
|
KNXTimeoutConstants[KNXTimeoutConstants["CONNECT_REQUEST_TIMEOUT"] = 10] = "CONNECT_REQUEST_TIMEOUT";
|
|
@@ -17,7 +17,8 @@ export declare class NPDU implements ServiceMessage {
|
|
|
17
17
|
private _hopCount;
|
|
18
18
|
TPDU: TPDU;
|
|
19
19
|
private _addressType;
|
|
20
|
-
|
|
20
|
+
length: number;
|
|
21
|
+
constructor(InstanceOfTPDU?: TPDU, addressType?: AddressType, hopCount?: number, length?: number);
|
|
21
22
|
/**
|
|
22
23
|
* Obtiene el Hop Count (0-7).
|
|
23
24
|
*/
|
|
@@ -19,10 +19,13 @@ class NPDU {
|
|
|
19
19
|
_hopCount = 6; // Valor por defecto estándar en KNX
|
|
20
20
|
TPDU; // Datos puros (payload del usuario)
|
|
21
21
|
_addressType;
|
|
22
|
-
|
|
22
|
+
length = 0;
|
|
23
|
+
constructor(InstanceOfTPDU = new TPDU_1.TPDU(), addressType = EnumControlFieldExtended_1.AddressType.GROUP, hopCount = 6, length) {
|
|
23
24
|
this._addressType = addressType;
|
|
24
25
|
this.hopCount = hopCount; // Usa el setter para validar
|
|
25
26
|
this.TPDU = InstanceOfTPDU;
|
|
27
|
+
if (length)
|
|
28
|
+
this.length = length;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* Obtiene el Hop Count (0-7).
|
|
@@ -54,7 +57,7 @@ class NPDU {
|
|
|
54
57
|
// Bits 7: Reservado (0) -> parece ser el AddressType
|
|
55
58
|
// Bits 6-4: Hop Count
|
|
56
59
|
// Bits 3-0: Length (Longitud del TPDU)
|
|
57
|
-
const npciByte = (this.addressType << 7) | (this._hopCount << 4) | (length & 0x0f);
|
|
60
|
+
const npciByte = (this.addressType << 7) | (this._hopCount << 4) | (this.length & 0x0f);
|
|
58
61
|
const buffer = Buffer.alloc(1 + length);
|
|
59
62
|
buffer.writeUInt8(npciByte, 0);
|
|
60
63
|
tpduBuffer.copy(buffer, 1);
|
|
@@ -97,7 +100,7 @@ class NPDU {
|
|
|
97
100
|
const tpdu = TPDU_1.TPDU.fromBuffer(tpduBuffer);
|
|
98
101
|
// 3. Retornar nueva instancia
|
|
99
102
|
// Nota: AddressType no viene en el NPDU, viene del cEMI. Asumimos GROUP por defecto.
|
|
100
|
-
return new NPDU(tpdu, EnumControlFieldExtended_1.AddressType.GROUP, hopCount);
|
|
103
|
+
return new NPDU(tpdu, EnumControlFieldExtended_1.AddressType.GROUP, hopCount, length);
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
106
|
exports.NPDU = NPDU;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,23 @@ export { KnxDataDecode } from "./core/data/KNXDataDecode";
|
|
|
2
2
|
export { KnxDataEncoder } from "./core/data/KNXDataEncode";
|
|
3
3
|
export { KNXnetIPServer } from "./connection/KNXnetIPServer";
|
|
4
4
|
export { KNXTunneling } from "./connection/KNXTunneling";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
5
|
+
export { KNXWebSocketGateway } from "./server/KNXWebSocketServer";
|
|
6
|
+
export { KNXMQTTGateway } from "./server/KNXMQTTGateway";
|
|
7
|
+
export * from "./connection/Router";
|
|
8
|
+
export * from "./connection/TPUART";
|
|
9
|
+
export * from "./connection/KNXUSBConnection";
|
|
10
|
+
export { CEMI, AdditionalInformationField } from "./core/CEMI";
|
|
11
|
+
export * from "./core/KNXAddInfoTypes";
|
|
12
|
+
export { EMI } from "./core/EMI";
|
|
7
13
|
export { knxLogger, setupLogger } from "./utils/Logger";
|
|
14
|
+
export * from "./@types/interfaces/DPTs";
|
|
15
|
+
export * from "./core/enum/APCIEnum";
|
|
16
|
+
export * from "./core/enum/EnumControlField";
|
|
17
|
+
export { AddressType } from "./core/enum/EnumControlFieldExtended";
|
|
18
|
+
export * from "./core/ControlField";
|
|
19
|
+
export * from "./core/ControlFieldExtended";
|
|
20
|
+
export * from "./core/layers/data/APDU";
|
|
21
|
+
export * from "./core/layers/data/NPDU";
|
|
22
|
+
export * from "./core/layers/data/TPDU";
|
|
23
|
+
export * from "./core/layers/interfaces/APCI";
|
|
24
|
+
export * from "./core/layers/interfaces/TPCI";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setupLogger = exports.knxLogger = exports.EMI = exports.CEMI = exports.KNXTunneling = exports.KNXnetIPServer = exports.KnxDataEncoder = exports.KnxDataDecode = void 0;
|
|
17
|
+
exports.AddressType = exports.setupLogger = exports.knxLogger = exports.EMI = exports.AdditionalInformationField = exports.CEMI = exports.KNXMQTTGateway = exports.KNXWebSocketGateway = exports.KNXTunneling = exports.KNXnetIPServer = exports.KnxDataEncoder = exports.KnxDataDecode = void 0;
|
|
4
18
|
var KNXDataDecode_1 = require("./core/data/KNXDataDecode");
|
|
5
19
|
Object.defineProperty(exports, "KnxDataDecode", { enumerable: true, get: function () { return KNXDataDecode_1.KnxDataDecode; } });
|
|
6
20
|
var KNXDataEncode_1 = require("./core/data/KNXDataEncode");
|
|
@@ -9,10 +23,31 @@ var KNXnetIPServer_1 = require("./connection/KNXnetIPServer");
|
|
|
9
23
|
Object.defineProperty(exports, "KNXnetIPServer", { enumerable: true, get: function () { return KNXnetIPServer_1.KNXnetIPServer; } });
|
|
10
24
|
var KNXTunneling_1 = require("./connection/KNXTunneling");
|
|
11
25
|
Object.defineProperty(exports, "KNXTunneling", { enumerable: true, get: function () { return KNXTunneling_1.KNXTunneling; } });
|
|
26
|
+
var KNXWebSocketServer_1 = require("./server/KNXWebSocketServer");
|
|
27
|
+
Object.defineProperty(exports, "KNXWebSocketGateway", { enumerable: true, get: function () { return KNXWebSocketServer_1.KNXWebSocketGateway; } });
|
|
28
|
+
var KNXMQTTGateway_1 = require("./server/KNXMQTTGateway");
|
|
29
|
+
Object.defineProperty(exports, "KNXMQTTGateway", { enumerable: true, get: function () { return KNXMQTTGateway_1.KNXMQTTGateway; } });
|
|
30
|
+
__exportStar(require("./connection/Router"), exports);
|
|
31
|
+
__exportStar(require("./connection/TPUART"), exports);
|
|
32
|
+
__exportStar(require("./connection/KNXUSBConnection"), exports);
|
|
12
33
|
var CEMI_1 = require("./core/CEMI");
|
|
13
34
|
Object.defineProperty(exports, "CEMI", { enumerable: true, get: function () { return CEMI_1.CEMI; } });
|
|
35
|
+
Object.defineProperty(exports, "AdditionalInformationField", { enumerable: true, get: function () { return CEMI_1.AdditionalInformationField; } });
|
|
36
|
+
__exportStar(require("./core/KNXAddInfoTypes"), exports);
|
|
14
37
|
var EMI_1 = require("./core/EMI");
|
|
15
38
|
Object.defineProperty(exports, "EMI", { enumerable: true, get: function () { return EMI_1.EMI; } });
|
|
16
39
|
var Logger_1 = require("./utils/Logger");
|
|
17
40
|
Object.defineProperty(exports, "knxLogger", { enumerable: true, get: function () { return Logger_1.knxLogger; } });
|
|
18
41
|
Object.defineProperty(exports, "setupLogger", { enumerable: true, get: function () { return Logger_1.setupLogger; } });
|
|
42
|
+
__exportStar(require("./@types/interfaces/DPTs"), exports);
|
|
43
|
+
__exportStar(require("./core/enum/APCIEnum"), exports);
|
|
44
|
+
__exportStar(require("./core/enum/EnumControlField"), exports);
|
|
45
|
+
var EnumControlFieldExtended_1 = require("./core/enum/EnumControlFieldExtended");
|
|
46
|
+
Object.defineProperty(exports, "AddressType", { enumerable: true, get: function () { return EnumControlFieldExtended_1.AddressType; } });
|
|
47
|
+
__exportStar(require("./core/ControlField"), exports);
|
|
48
|
+
__exportStar(require("./core/ControlFieldExtended"), exports);
|
|
49
|
+
__exportStar(require("./core/layers/data/APDU"), exports);
|
|
50
|
+
__exportStar(require("./core/layers/data/NPDU"), exports);
|
|
51
|
+
__exportStar(require("./core/layers/data/TPDU"), exports);
|
|
52
|
+
__exportStar(require("./core/layers/interfaces/APCI"), exports);
|
|
53
|
+
__exportStar(require("./core/layers/interfaces/TPCI"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MQTTGatewayOptions } from "../@types/interfaces/servers";
|
|
2
|
+
export declare class KNXMQTTGateway {
|
|
3
|
+
private options;
|
|
4
|
+
private aedesBroker;
|
|
5
|
+
private server;
|
|
6
|
+
private client;
|
|
7
|
+
private topicPrefix;
|
|
8
|
+
constructor(options: MQTTGatewayOptions);
|
|
9
|
+
start(): Promise<void>;
|
|
10
|
+
private handleMQTTMessage;
|
|
11
|
+
private publishError;
|
|
12
|
+
stop(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.KNXMQTTGateway = void 0;
|
|
37
|
+
const aedes_1 = require("aedes");
|
|
38
|
+
const aedes_server_factory_1 = require("aedes-server-factory");
|
|
39
|
+
const mqtt = __importStar(require("mqtt"));
|
|
40
|
+
const GroupAddressCache_1 = require("../core/cache/GroupAddressCache");
|
|
41
|
+
class KNXMQTTGateway {
|
|
42
|
+
options;
|
|
43
|
+
aedesBroker = null;
|
|
44
|
+
server = null;
|
|
45
|
+
client = null;
|
|
46
|
+
topicPrefix;
|
|
47
|
+
constructor(options) {
|
|
48
|
+
this.options = options;
|
|
49
|
+
this.topicPrefix = options.topicPrefix || "knx";
|
|
50
|
+
}
|
|
51
|
+
async start() {
|
|
52
|
+
// 1. Setup embedded broker if requested
|
|
53
|
+
if (this.options.embeddedBroker) {
|
|
54
|
+
this.aedesBroker = new aedes_1.Aedes();
|
|
55
|
+
this.server = (0, aedes_server_factory_1.createServer)(this.aedesBroker);
|
|
56
|
+
const port = this.options.embeddedBroker.port;
|
|
57
|
+
const host = this.options.embeddedBroker.host || "127.0.0.1";
|
|
58
|
+
await new Promise((serverResolve) => {
|
|
59
|
+
this.server.listen(port, host, () => {
|
|
60
|
+
serverResolve();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
await new Promise((resolve) => {
|
|
65
|
+
// 2. Setup Client to bridge KNX to MQTT
|
|
66
|
+
const url = this.options.brokerUrl ||
|
|
67
|
+
(this.options.embeddedBroker ? `mqtt://127.0.0.1:${this.options.embeddedBroker.port}` : null);
|
|
68
|
+
if (!url) {
|
|
69
|
+
throw new Error("No Broker URL provided nor embedded broker requested.");
|
|
70
|
+
}
|
|
71
|
+
this.client = mqtt.connect(url, {
|
|
72
|
+
username: this.options.mqttUsername,
|
|
73
|
+
password: this.options.mqttPassword,
|
|
74
|
+
});
|
|
75
|
+
this.client.on("connect", () => {
|
|
76
|
+
// Subscribe to command topics: prefix/command/action/groupAddress
|
|
77
|
+
// Actions: write, read, config_dpt
|
|
78
|
+
this.client?.subscribe(`${this.topicPrefix}/command/write/+`);
|
|
79
|
+
this.client?.subscribe(`${this.topicPrefix}/command/read/+`);
|
|
80
|
+
this.client?.subscribe(`${this.topicPrefix}/command/config_dpt/+`);
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
this.client.on("message", (topic, message) => {
|
|
84
|
+
this.handleMQTTMessage(topic, message);
|
|
85
|
+
});
|
|
86
|
+
// Global Listener from KNXContext
|
|
87
|
+
this.options.knxContext.on("indication", (cemi) => {
|
|
88
|
+
if (!("destinationAddress" in cemi))
|
|
89
|
+
return;
|
|
90
|
+
const dest = cemi.destinationAddress;
|
|
91
|
+
if (!dest)
|
|
92
|
+
return;
|
|
93
|
+
const cache = GroupAddressCache_1.GroupAddressCache.getInstance();
|
|
94
|
+
const entries = cache.query(dest, undefined, undefined, true);
|
|
95
|
+
let decodedValue = undefined;
|
|
96
|
+
if (entries && entries.length > 0) {
|
|
97
|
+
decodedValue = entries[0].decodedValue;
|
|
98
|
+
}
|
|
99
|
+
// Publish to mqtt
|
|
100
|
+
const pubTopic = `${this.topicPrefix}/state/${dest}`;
|
|
101
|
+
const payload = JSON.stringify({
|
|
102
|
+
decodedValue: decodedValue,
|
|
103
|
+
});
|
|
104
|
+
if (this.client?.connected) {
|
|
105
|
+
this.client.publish(pubTopic, payload, { retain: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
handleMQTTMessage(topic, message) {
|
|
111
|
+
const parts = topic.split("/");
|
|
112
|
+
// knx/command/write/1/2/3
|
|
113
|
+
if (parts.length < 4)
|
|
114
|
+
return;
|
|
115
|
+
const action = parts[2];
|
|
116
|
+
const groupAddress = parts.slice(3).join("/"); // To handle 1/2/3
|
|
117
|
+
let payload = {};
|
|
118
|
+
try {
|
|
119
|
+
const msgStr = message.toString();
|
|
120
|
+
payload = msgStr ? JSON.parse(msgStr) : {};
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
// simple value fallback
|
|
125
|
+
payload = { value: message.toString() };
|
|
126
|
+
}
|
|
127
|
+
const { value, dpt } = payload;
|
|
128
|
+
if (action === "config_dpt" && dpt) {
|
|
129
|
+
GroupAddressCache_1.GroupAddressCache.getInstance().setAddressDPT(groupAddress, dpt);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (action === "read") {
|
|
133
|
+
GroupAddressCache_1.GroupAddressCache.getInstance()
|
|
134
|
+
.readDirectAsync(groupAddress, this.options.knxContext)
|
|
135
|
+
.catch((err) => {
|
|
136
|
+
this.publishError(groupAddress, err.message);
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (action === "write" && value !== undefined) {
|
|
141
|
+
const targetDpt = dpt || GroupAddressCache_1.GroupAddressCache.getInstance().getAddressDPT(groupAddress);
|
|
142
|
+
this.options.knxContext.write(groupAddress, targetDpt, value).catch((err) => {
|
|
143
|
+
this.publishError(groupAddress, err.message);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
publishError(address, error) {
|
|
148
|
+
if (!this.client?.connected)
|
|
149
|
+
return;
|
|
150
|
+
this.client.publish(`${this.topicPrefix}/error`, JSON.stringify({ groupAddress: address, error }));
|
|
151
|
+
}
|
|
152
|
+
stop() {
|
|
153
|
+
if (this.client) {
|
|
154
|
+
this.client.end();
|
|
155
|
+
}
|
|
156
|
+
if (this.server) {
|
|
157
|
+
this.server.close();
|
|
158
|
+
}
|
|
159
|
+
if (this.aedesBroker) {
|
|
160
|
+
this.aedesBroker.close();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.KNXMQTTGateway = KNXMQTTGateway;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { WebSocketGatewayOptions } from "../@types/interfaces/servers";
|
|
2
|
+
export declare class KNXWebSocketGateway {
|
|
3
|
+
private wss;
|
|
4
|
+
private options;
|
|
5
|
+
private activeSubscriptions;
|
|
6
|
+
constructor(options: WebSocketGatewayOptions);
|
|
7
|
+
start(): void;
|
|
8
|
+
private handleClientMessage;
|
|
9
|
+
private broadcast;
|
|
10
|
+
private sendError;
|
|
11
|
+
stop(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KNXWebSocketGateway = void 0;
|
|
4
|
+
const ws_1 = require("ws");
|
|
5
|
+
const GroupAddressCache_1 = require("../core/cache/GroupAddressCache");
|
|
6
|
+
class KNXWebSocketGateway {
|
|
7
|
+
wss = null;
|
|
8
|
+
options;
|
|
9
|
+
// Track subscribed addresses: "*" means all
|
|
10
|
+
activeSubscriptions = new Set();
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
start() {
|
|
15
|
+
this.wss = new ws_1.WebSocketServer({ port: this.options.port });
|
|
16
|
+
this.wss.on("connection", (ws) => {
|
|
17
|
+
ws.on("message", (message) => {
|
|
18
|
+
try {
|
|
19
|
+
const payload = JSON.parse(message.toString());
|
|
20
|
+
this.handleClientMessage(ws, payload);
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
this.sendError(ws, "Invalid JSON format");
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
// Acknowledge connection
|
|
28
|
+
ws.send(JSON.stringify({ action: "connected", message: "KNX WebSocket Gateway connected" }));
|
|
29
|
+
});
|
|
30
|
+
// Listen globally for events from knxContext to dispatch to WS clients
|
|
31
|
+
this.options.knxContext.on("indication", (cemi) => {
|
|
32
|
+
if (!("destinationAddress" in cemi))
|
|
33
|
+
return;
|
|
34
|
+
const dest = cemi.destinationAddress;
|
|
35
|
+
if (!dest)
|
|
36
|
+
return;
|
|
37
|
+
if (this.activeSubscriptions.has(dest) || this.activeSubscriptions.has("*")) {
|
|
38
|
+
const cache = GroupAddressCache_1.GroupAddressCache.getInstance();
|
|
39
|
+
const entries = cache.query(dest, undefined, undefined, true);
|
|
40
|
+
let decodedValue = undefined;
|
|
41
|
+
if (entries && entries.length > 0) {
|
|
42
|
+
decodedValue = entries[0].decodedValue;
|
|
43
|
+
}
|
|
44
|
+
this.broadcast(dest, cemi, decodedValue);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
handleClientMessage(ws, payload) {
|
|
49
|
+
const { action, groupAddress, value, dpt } = payload;
|
|
50
|
+
if (action === "config_dpt" && groupAddress && dpt) {
|
|
51
|
+
GroupAddressCache_1.GroupAddressCache.getInstance().setAddressDPT(groupAddress, dpt);
|
|
52
|
+
ws.send(JSON.stringify({ action: "config_dpt_ack", groupAddress, dpt }));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (action === "read" && groupAddress) {
|
|
56
|
+
GroupAddressCache_1.GroupAddressCache.getInstance()
|
|
57
|
+
.readDirectAsync(groupAddress, this.options.knxContext)
|
|
58
|
+
.then((res) => {
|
|
59
|
+
ws.send(JSON.stringify({ action: "read_result", groupAddress, data: res }));
|
|
60
|
+
})
|
|
61
|
+
.catch((err) => this.sendError(ws, err.message));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (action === "query" && groupAddress) {
|
|
65
|
+
const results = GroupAddressCache_1.GroupAddressCache.getInstance().query(groupAddress, payload.startDate ? new Date(payload.startDate) : undefined, payload.endDate ? new Date(payload.endDate) : undefined, payload.onlyLatest ?? false);
|
|
66
|
+
ws.send(JSON.stringify({ action: "query_result", groupAddress, results }));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (action === "write" && groupAddress && value !== undefined) {
|
|
70
|
+
// Try getting dpt from payload, or fallback to cache
|
|
71
|
+
const targetDpt = dpt || GroupAddressCache_1.GroupAddressCache.getInstance().getAddressDPT(groupAddress);
|
|
72
|
+
// Context .write naturally encodes using KNXDataEncoder internally if dpt is provided
|
|
73
|
+
this.options.knxContext
|
|
74
|
+
.write(groupAddress, targetDpt, value)
|
|
75
|
+
.then(() => {
|
|
76
|
+
ws.send(JSON.stringify({ action: "write_ack", groupAddress }));
|
|
77
|
+
})
|
|
78
|
+
.catch((err) => {
|
|
79
|
+
this.sendError(ws, err.message);
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (action === "subscribe") {
|
|
84
|
+
const target = groupAddress || "*";
|
|
85
|
+
this.activeSubscriptions.add(target);
|
|
86
|
+
ws.send(JSON.stringify({ action: "subscribe_ack", groupAddress: target }));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (action === "unsubscribe") {
|
|
90
|
+
const target = groupAddress || "*";
|
|
91
|
+
this.activeSubscriptions.delete(target);
|
|
92
|
+
ws.send(JSON.stringify({ action: "unsubscribe_ack", groupAddress: target }));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
broadcast(address, cemi, decodedValue) {
|
|
97
|
+
if (!this.wss)
|
|
98
|
+
return;
|
|
99
|
+
const msg = JSON.stringify({
|
|
100
|
+
action: "event",
|
|
101
|
+
groupAddress: address,
|
|
102
|
+
decodedValue,
|
|
103
|
+
});
|
|
104
|
+
this.wss.clients.forEach((client) => {
|
|
105
|
+
if (client.readyState === ws_1.WebSocket.OPEN) {
|
|
106
|
+
client.send(msg);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
sendError(ws, message) {
|
|
111
|
+
ws.send(JSON.stringify({ action: "error", message }));
|
|
112
|
+
}
|
|
113
|
+
stop() {
|
|
114
|
+
if (this.wss)
|
|
115
|
+
this.wss.close();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.KNXWebSocketGateway = KNXWebSocketGateway;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CEMIInstance } from "../core/CEMI";
|
|
2
|
+
import { EMIInstance } from "../core/EMI";
|
|
2
3
|
/**
|
|
3
4
|
* Adapter utility to convert between EMI (External Message Interface) and cEMI (Common External Message Interface).
|
|
4
5
|
* EMI is typically used by physical interfaces like TPUART, while cEMI is standard for KNXnet/IP.
|
|
@@ -8,9 +9,9 @@ export declare class CEMIAdapter {
|
|
|
8
9
|
* Adapts a raw EMI2 message (Buffer) to a cEMI instance.
|
|
9
10
|
* Typical EMI structure: [MC] [Ctrl] [Src] [Dst] [Len] [NPDU(NPCI + TPDU)]
|
|
10
11
|
*/
|
|
11
|
-
static emiToCemi(emiBuffer: Buffer):
|
|
12
|
+
static emiToCemi(emiBuffer: Buffer): CEMIInstance | null;
|
|
12
13
|
/**
|
|
13
14
|
* Converts a cEMI instance to an EMI2 compatible Buffer or ServiceMessage.
|
|
14
15
|
*/
|
|
15
|
-
static cemiToEmi(cemi:
|
|
16
|
+
static cemiToEmi(cemi: CEMIInstance): EMIInstance | null;
|
|
16
17
|
}
|