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.
Files changed (50) hide show
  1. package/LICENSE +51 -21
  2. package/README.md +274 -61
  3. package/dist/@types/interfaces/connection.d.ts +80 -13
  4. package/dist/@types/interfaces/servers.d.ts +18 -0
  5. package/dist/@types/interfaces/servers.js +2 -0
  6. package/dist/connection/KNXService.d.ts +13 -30
  7. package/dist/connection/KNXService.js +4 -164
  8. package/dist/connection/KNXTunneling.d.ts +4 -4
  9. package/dist/connection/KNXTunneling.js +35 -62
  10. package/dist/connection/KNXUSBConnection.d.ts +20 -0
  11. package/dist/connection/KNXUSBConnection.js +358 -0
  12. package/dist/connection/KNXnetIPServer.d.ts +29 -12
  13. package/dist/connection/KNXnetIPServer.js +261 -83
  14. package/dist/connection/Router.d.ts +52 -32
  15. package/dist/connection/Router.js +225 -153
  16. package/dist/connection/TPUART.d.ts +8 -3
  17. package/dist/connection/TPUART.js +41 -37
  18. package/dist/connection/TunnelConnection.d.ts +3 -1
  19. package/dist/connection/TunnelConnection.js +6 -4
  20. package/dist/core/CEMI.d.ts +7 -2
  21. package/dist/core/CEMI.js +5 -8
  22. package/dist/core/EMI.d.ts +312 -200
  23. package/dist/core/EMI.js +511 -1007
  24. package/dist/core/KNXnetIPStructures.d.ts +10 -1
  25. package/dist/core/KNXnetIPStructures.js +15 -10
  26. package/dist/core/MessageCodeField.d.ts +1 -1
  27. package/dist/core/cache/GroupAddressCache.d.ts +57 -0
  28. package/dist/core/cache/GroupAddressCache.js +227 -0
  29. package/dist/core/data/KNXDataDecode.d.ts +2 -2
  30. package/dist/core/data/KNXDataDecode.js +198 -183
  31. package/dist/core/enum/EnumControlField.d.ts +0 -5
  32. package/dist/core/enum/EnumControlField.js +1 -7
  33. package/dist/core/enum/EnumControlFieldExtended.d.ts +1 -1
  34. package/dist/core/enum/EnumShortACKFrame.d.ts +1 -1
  35. package/dist/core/enum/ErrorCodeSet.js +59 -0
  36. package/dist/core/enum/KNXnetIPEnum.d.ts +2 -2
  37. package/dist/core/enum/KNXnetIPEnum.js +19 -1
  38. package/dist/core/layers/data/NPDU.d.ts +2 -1
  39. package/dist/core/layers/data/NPDU.js +6 -3
  40. package/dist/index.d.ts +19 -2
  41. package/dist/index.js +36 -1
  42. package/dist/server/KNXMQTTGateway.d.ts +13 -0
  43. package/dist/server/KNXMQTTGateway.js +164 -0
  44. package/dist/server/KNXWebSocketServer.d.ts +12 -0
  45. package/dist/server/KNXWebSocketServer.js +118 -0
  46. package/dist/utils/CEMIAdapter.d.ts +4 -3
  47. package/dist/utils/CEMIAdapter.js +26 -30
  48. package/dist/utils/Logger.d.ts +4 -4
  49. package/dist/utils/Logger.js +3 -7
  50. 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.FrameKind = exports.FrameType = exports.Priority = void 0;
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,4 +1,4 @@
1
- export declare enum ShortAckCode {
1
+ export declare const enum ShortAckCode {
2
2
  ACK = 204,
3
3
  NAK = 12,
4
4
  BUSY = 192,
@@ -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
- constructor(InstanceOfTPDU?: TPDU, addressType?: AddressType, hopCount?: number);
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
- constructor(InstanceOfTPDU = new TPDU_1.TPDU(), addressType = EnumControlFieldExtended_1.AddressType.GROUP, hopCount = 6) {
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 { CEMI } from "./core/CEMI";
6
- export { EMI } from './core/EMI';
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 { ServiceMessage } from "../@types/interfaces/ServiceMessage";
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): ServiceMessage | null;
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: any): ServiceMessage | null;
16
+ static cemiToEmi(cemi: CEMIInstance): EMIInstance | null;
16
17
  }