@jotiotech/node-red-sia-premise-device 0.2.1

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/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # SIA Device
2
+
3
+ TODO
4
+
5
+ Warning: ONLY ASCII characters are supported, if you ever try to use UTF8 it will not be converted to Windows-1252 and on the receiving end you will end up with gibberish.
6
+
package/icon.png ADDED
Binary file
package/icon.svg ADDED
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
2
+ <path d="
3
+ m 24 26.05 M 9 45 Q 7.8 45 6.9 44.1 Q 6 43.2 6 42 V 5 q 0 -1.2 0.9 -2.1 q 0.9 -0.9 2.1 -0.9 h 30 q 1.2 0 2.1 0.9 q 0.9 0.9 0.9 2.1 V 25.7 Q 41.3 25.3 40.55 25.075 Q 39.8 24.85 39 24.65 V 5 H 9 V 42 H 24.3 Q 24.65 42.85 25.05 43.575 T 26 45 Z M 34.65 47 L 34.4 44.6 Q 33.4 44.3 32.35 43.725 Q 31.3 43.15 30.65 42.45 L 28.55 43.45 L 26.8 40.75 L 28.7 39.25 Q 28.45 38.1 28.45 37.175 Q 28.45 36.25 28.7 35.1 L 26.8 33.6 L 28.55 30.85 L 30.65 31.85 Q 31.3 31.25 32.35 30.65 Q 33.4 30.05 34.4 29.75 L 34.65 27.3 H 37.65 L 37.95 29.75 Q 38.95 30.05 40 30.65 Q 41.05 31.25 41.7 31.85 L 43.8 30.85 L 45.55 33.6 L 43.65 35.1 Q 43.9 36.25 43.9 37.175 Q 43.9 38.1 43.65 39.25 L 45.55 40.75 L 43.8 43.45 L 41.7 42.45 Q 41.05 43.15 40 43.725 Q 38.95 44.3 37.95 44.6 L 37.65 47 Z M 36.15 42.25 Q 38.35 42.25 39.8 40.8 Q 41.25 39.35 41.25 37.15 T 39.8 33.5 Q 38.35 32.05 36.15 32.05 T 32.5 33.5 Q 31.05 34.95 31.05 37.15 T 32.5 40.8 Q 33.95 42.25 36.15 42.25 Z
4
+ " />
5
+ </svg>
6
+
7
+ <!--
8
+ M24 21.05ZM9 40q-1.2 0-2.1-.9Q6 38.2 6 37V7q0-1.2.9-2.1Q7.8 4 9 4h30q1.2 0 2.1.9.9.9.9 2.1v13.7q-.7-.4-1.45-.625-.75-.225-1.55-.425V7H9v28.15q2.95-2.75 6.675-4.475Q19.4 28.95 23.75 28.9q-.2.7-.3 1.45-.1.75-.1 1.55-3.15.1-6.1 1.275-2.95 1.175-5.5 3.375V37H24.3q.35.85.75 1.575T26 40Zm15.1-15.05q.4 0 .775-.025t.775-.125q.95-1.3 2.175-2.375Q29.05 21.35 30.5 20.65q.25-.6.375-1.25T31 18.05q0-2.9-2-4.9-2-2-4.9-2-2.9 0-4.9 2-2 2-2 4.9 0 2.9 2 4.9 2 2 4.9 2Zm0-3q-1.6 0-2.75-1.15t-1.15-2.75q0-1.6 1.15-2.75t2.75-1.15q1.6 0 2.75 1.15T28 18.05q0 1.6-1.15 2.75t-2.75 1.15ZM34.65 42l-.25-2.4q-1-.3-2.05-.875-1.05-.575-1.7-1.275l-2.1 1-1.75-2.7 1.9-1.5q-.25-1.15-.25-2.075 0-.925.25-2.075l-1.9-1.5 1.75-2.75 2.1 1q.65-.6 1.7-1.2 1.05-.6 2.05-.9l.25-2.45h3l.3 2.45q1 .3 2.05.9 1.05.6 1.7 1.2l2.1-1 1.75 2.75-1.9 1.5q.25 1.15.25 2.075 0 .925-.25 2.075l1.9 1.5-1.75 2.7-2.1-1q-.65.7-1.7 1.275-1.05.575-2.05.875l-.3 2.4Zm1.5-4.75q2.2 0 3.65-1.45 1.45-1.45 1.45-3.65T39.8 28.5q-1.45-1.45-3.65-1.45T32.5 28.5q-1.45 1.45-1.45 3.65t1.45 3.65q1.45 1.45 3.65 1.45Z
9
+ -->
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@jotiotech/node-red-sia-premise-device",
3
+ "version": "0.2.1",
4
+ "engines": {
5
+ "node": ">=12.0.0"
6
+ },
7
+ "license": "MIT",
8
+ "description": "",
9
+ "dependencies": {
10
+ "retry": "^0.13.1"
11
+ },
12
+ "keywords": [
13
+ "node-red",
14
+ "parsing"
15
+ ],
16
+ "node-red": {
17
+ "version": ">=1.0.0",
18
+ "nodes": {
19
+ "SIA_device": "src/SIA_device.js",
20
+ "SIA_server": "src/SIA_server.js"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,44 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('SIA-device', {
3
+ category: 'common',
4
+ color: '#0B99D8',
5
+ defaults: {
6
+ name: { value: "" },
7
+ server: { value: "", type: "SIA-server", required: true }, // This will
8
+ },
9
+ align: 'left',
10
+ icon: "icon.png",
11
+ inputs: 1,
12
+ outputs: 0,
13
+ label: function () {
14
+ return this.name || "SIA Device";
15
+ },
16
+
17
+ });
18
+
19
+ </script>
20
+
21
+ <style>
22
+
23
+ .input-button-wrap>input {
24
+ width: 100% !important;
25
+ }
26
+ select {
27
+ width: 100% !important;
28
+ }
29
+ .editor-button {
30
+ height: 30px;
31
+ padding: 0 10px;
32
+ }
33
+ </style>
34
+
35
+ <script type="text/x-red" data-template-name="SIA-device">
36
+ <div class="form-row">
37
+ <label for="node-input-name"><i class="icon-tag"></i> Name</label>
38
+ <input type="text" id="node-input-name" placeholder="Name">
39
+ </div>
40
+ <div class="form-row">
41
+ <label for="node-input-server"><i class="icon-globe"></i> <span data-i18n="modbus-contrib.label.server"></span></label>
42
+ <input type="text" id="node-input-server">
43
+ </div>
44
+ </script>
@@ -0,0 +1,240 @@
1
+ // const { Iconv } = require('iconv');
2
+
3
+
4
+ module.exports = function (RED) {
5
+ const tableCRC = [
6
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
7
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
8
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
9
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
10
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
11
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
12
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
13
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
14
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
15
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
16
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
17
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
18
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
19
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
20
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
21
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
22
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
23
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
24
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
25
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
26
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
27
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
28
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
29
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
30
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
31
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
32
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
33
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
34
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
35
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
36
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
37
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
38
+ ];
39
+
40
+ const deviceMap = new Map();
41
+ const crypto = require('crypto');
42
+
43
+
44
+
45
+ function SIADeviceNode(config) {
46
+ RED.nodes.createNode(this, config);
47
+
48
+ // TODO: eventually we should disconnect from the server after each message as there shouldn't be a high volume of messages
49
+ const server = RED.nodes.getNode(config.server);
50
+
51
+ const siaConfig = server.getSIAConfig();
52
+ console.log('SIA Config:', siaConfig);
53
+
54
+
55
+ server.connect();
56
+
57
+
58
+
59
+ function utf8toWin1252(str) {
60
+ tmpBuf = Buffer.from(str, 'utf8');
61
+ return tmpBuf.toString('latin1');
62
+ }
63
+
64
+ function calculateCRCIBM16(str){
65
+ data = new Buffer.from(str);
66
+ let len = data.length;
67
+ let buffer = 0;
68
+ let crc;
69
+ while (len--) {
70
+ crc = ((crc >>> 8) ^ (tableCRC[(crc ^ (data[buffer++])) & 0xff]));
71
+ }
72
+ return crc.toString(16).padStart(4, '0').toUpperCase();
73
+ }
74
+
75
+ function encrypt(encryptionType, encryptionKey, str) {
76
+ try {
77
+ const encryptionKeyBuf = new Buffer.from(encryptionKey, 'hex');
78
+ let iv = new Buffer.alloc(16);
79
+ iv.fill(0);
80
+ let cipher = crypto.createCipheriv(encryptionType, encryptionKeyBuf, iv);
81
+ cipher.setAutoPadding(false);
82
+ let encoded = cipher.update(str, 'utf8', 'hex');
83
+ encoded += cipher.final('hex');
84
+ return (encoded ? encoded : undefined);
85
+ } catch (e) {
86
+ return undefined;
87
+ }
88
+ }
89
+
90
+
91
+ function generateTimestamp(){
92
+ const now = new Date();
93
+
94
+ // Pad single-digit numbers with a leading zero
95
+ const pad = (num) => num.toString().padStart(2, '0');
96
+
97
+ const hours = pad(now.getHours());
98
+ const minutes = pad(now.getMinutes());
99
+ const seconds = pad(now.getSeconds());
100
+ const month = pad(now.getMonth() + 1); // Months are 0-indexed
101
+ const day = pad(now.getDate());
102
+ const year = now.getFullYear();
103
+
104
+ return `_${hours}:${minutes}:${seconds},${month}-${day}-${year}`;
105
+
106
+ }
107
+
108
+ function generatePadding(data){
109
+ buffer = new Buffer.from(data); // we need to get byte length of the message body, not string length
110
+ let msgLength = buffer.length;
111
+
112
+ let padding = '';
113
+ if(msgLength % 16 !== 0){
114
+ const paddingNeeded = 15 - (msgLength % 16);
115
+ // generate random uppercase letters (only letters) for paddingNeeded
116
+ for(let i=0; i<paddingNeeded; i++){
117
+ const randomChar = String.fromCharCode(65 + Math.floor(Math.random() * 26));
118
+ padding += randomChar;
119
+ }
120
+ // generate byte array
121
+ console.log('Padding needed:', paddingNeeded, 'Generated padding:', padding);
122
+ return padding+'|';
123
+ }else
124
+ return '';
125
+ }
126
+
127
+
128
+ function validateHexString(str, strName, minLength, maxLength){ // NOTE LGTM
129
+ if(str.length < minLength || str.length > maxLength){
130
+ console.error(`${strName} must be between ${minLength} and ${maxLength} characters long`);
131
+ return false;
132
+
133
+ }
134
+
135
+ if(str.length !=0 && !/^[0-9A-Fa-f]+$/.test(str)){
136
+ console.error(`${strName} must be hexadecimal characters only (0-9, A-F)`);
137
+ return false;
138
+ }
139
+ return true;
140
+ }
141
+
142
+ function iterateMsgCount(id){ // NOTE LGTM
143
+ if(!deviceMap.has(id)){
144
+ deviceMap.set(id, 1);
145
+ }else{
146
+ let count = deviceMap.get(id);
147
+ count = (count % 9999) + 1; // wrap around at 999
148
+ deviceMap.set(id, count);
149
+ }
150
+ }
151
+
152
+ // utf8toWin1252("a test ñ");
153
+
154
+
155
+ this.on('input', message => {
156
+ // const payload = message.payload;
157
+
158
+ let payload = {
159
+ "account" : "AABBCC",
160
+ "accountPrefix" : "5678"
161
+ }
162
+ siaConfig.receiverNumber = "1234";
163
+
164
+ const deviceAccount = payload.account;
165
+ const deviceAccountPrefix = payload.accountPrefix || '0';
166
+
167
+ // INPUT VALIDATION
168
+
169
+ if(!validateHexString(deviceAccount, 'Device account', 3, 16)) return;
170
+ if(!validateHexString(deviceAccountPrefix, 'Device account prefix', 1, 6)) return;
171
+ if(!validateHexString(siaConfig.receiverNumber, 'Receiver number', 0, 6)) return;
172
+
173
+ const deviceIdentifier = 'L'+ deviceAccountPrefix + '#' + deviceAccount;
174
+
175
+ iterateMsgCount(deviceIdentifier);
176
+
177
+
178
+ // PART: <"id"><seq><Rrcvr><Lpref><#acct>[
179
+ let messageBodyStart = '"'; // <"id"><seq><Rrcvr><Lpref><#acct>[
180
+ if(siaConfig.encryptionEnabled)
181
+ messageBodyStart += '*';
182
+
183
+ messageBodyStart += 'SIA-DCS"' // ID placeholder
184
+ messageBodyStart += deviceMap.get(deviceIdentifier).toString().padStart(4, '0'); // message sequence number
185
+ if(siaConfig.receiverNumber.length >0)
186
+ messageBodyStart += 'R'+siaConfig.receiverNumber;
187
+ messageBodyStart += deviceIdentifier;
188
+ messageBodyStart += '[';
189
+
190
+
191
+ // PART: #<acct>|<data>][<extende data>]
192
+ let messageBodyData = '#'+deviceAccount + '|'; // <pad>|...data...][x…data…]
193
+
194
+ // data format
195
+ // (N)(id-number)(DCS)(zone)
196
+ // there's also a format of group-zone
197
+
198
+ // messageBodyData += "NFA"; //
199
+ messageBodyData += "Nri129/FA1234"; //
200
+ // optional extended data can be added here, for example
201
+
202
+ // PART: ]<timestamp>, finalization of body
203
+ let messageBody = ''; // <"id"><seq><Rrcvr><Lpref><#acct>[<pad>|...data...][x…data…]<timestamp>
204
+ let timestamp = generateTimestamp(); // TODO: this should be optional
205
+
206
+ if(siaConfig.encryptionEnabled){ // everything past [ till <CR> is to be encrypted
207
+ let toEncrypt = messageBodyData+']'+timestamp;
208
+ toEncrypt = generatePadding(toEncrypt) + toEncrypt;
209
+ messageBody = messageBodyStart + encrypt(siaConfig.encryptionType, siaConfig.encryptionKey, toEncrypt);
210
+ }else{
211
+ messageBody += messageBodyStart;
212
+ messageBody += messageBodyData;
213
+ messageBody += ']'; // end of data fields
214
+ // optional extended data
215
+ messageBody += timestamp;
216
+ }
217
+
218
+ // Append <LF><crc><>
219
+ msgCount = (new Buffer.from(messageBody)).length;
220
+
221
+ let msg = '\n'; // <LF><crc><0LLL><"id"><seq><Rrcvr><Lpref><#acct>[<pad>|...data...][x…data…]<timestamp><CR>
222
+ msg += calculateCRCIBM16(messageBody); // CRC is 4 ASCII characters
223
+ msg += '0'+msgCount.toString(16).toUpperCase().padStart(3, '0'); // Carriage return
224
+ msg += messageBody;
225
+ msg += '\r';
226
+
227
+ // let hexMsg = '';
228
+ // for(let i=0; i<msg.length; i++){
229
+ // hexMsg += msg.charCodeAt(i).toString(16).padStart(2, '0').toUpperCase() + ' ';
230
+ // }
231
+ // console.log('Final message in hex: ', hexMsg);
232
+
233
+ server.connect();
234
+ server.write(msg);
235
+ server.close();
236
+ });
237
+ }
238
+
239
+ RED.nodes.registerType('SIA-device', SIADeviceNode);
240
+ };
@@ -0,0 +1,75 @@
1
+ <!--
2
+ Copyright (c) since the year 2016 Klaus Landsdorf (http://plus4nodered.com/)
3
+ Copyright 2016 - Jason D. Harper, Argonne National Laboratory
4
+ Copyright 2015,2016 - Mika Karaila, Valmet Automation Inc.
5
+ All rights reserved.
6
+ node-red-contrib-modbus
7
+
8
+ @author <a href="mailto:klaus.landsdorf@bianco-royal.de">Klaus Landsdorf</a> (Bianco Royal)
9
+ -->
10
+ <script type="text/javascript">
11
+ RED.nodes.registerType('SIA-server', {
12
+ category: 'config',
13
+ defaults: {
14
+
15
+ name: { value: "" },
16
+ receiverHost: { value: "", required: true },
17
+ receiverPort: { value: "", required: true },
18
+ receiverNumber : { value: ""},
19
+ encryptionKey: { value: ""},
20
+ },
21
+ label: function () {
22
+ return this.name || 'sia-server@' + this.receiverHost;
23
+ },
24
+
25
+ oneditprepare: function () {
26
+ let previous = null;
27
+
28
+
29
+ if(this.encryptionKey == null){
30
+ this.encryptionKey = "";
31
+ }
32
+
33
+ let allowedLengths = [0, 32, 48, 64];
34
+ if(!allowedLengths.includes(this.encryptionKey.length)){
35
+ Node.error('Encryption key must be empty or 128, 192, 256 bits long in HEX');
36
+ }
37
+
38
+ if(!/^[0-9A-Fa-f]+$/.test(this.encryptionKey)){ // hexadecimal characters only
39
+ Node.error('Encryption key must be in hexadecimal characters only (0-9, A-F)');
40
+ }
41
+
42
+
43
+
44
+ // check if encryptionKey is 128 bit 192 or 256 bit long
45
+ // encryptionKey needs to be a hex string
46
+
47
+
48
+
49
+ }
50
+ })
51
+ </script>
52
+
53
+ <script type="text/x-red" data-template-name="SIA-server">
54
+ <div class="form-row">
55
+ <label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
56
+ <input type="text" id="node-config-input-name" placeholder="Name">
57
+ </div>
58
+ <div class="form-row">
59
+ <label for="node-config-input-receiverHost">Receiver IP</label>
60
+ <input type="text" id="node-config-input-receiverHost" placeholder="Receiver Host">
61
+ </div>
62
+ <div class="form-row">
63
+ <label for="node-config-input-receiverPort">Receiver Port</label>
64
+ <input type="number" id="node-config-input-receiverPort" placeholder="Receiver Port">
65
+ </div>
66
+ <div class="form-row">
67
+ <label for="node-config-input-receiverNumber">Receiver Number</label>
68
+ <input type="number" id="node-config-input-receiverNumber" placeholder="Receiver Number">
69
+ </div>
70
+ <div class="form-row">
71
+ <label for="node-config-input-encryptionKey">Encryption Key</label>
72
+ <input type="password" id="node-config-input-encryptionKey" placeholder="SIA Password">
73
+ </div>
74
+ </script>
75
+
@@ -0,0 +1,127 @@
1
+ module.exports = function (RED) {
2
+ 'use strict';
3
+ // SOURCE-MAP-REQUIRED
4
+
5
+ const Net = require('node:net');
6
+ const Retry = require('retry');
7
+
8
+ function SIAServer(config) {
9
+ RED.nodes.createNode(this, config);
10
+
11
+ this.receiverHost = config.receiverHost;
12
+ this.receiverPort = Number.parseInt(config.receiverPort);
13
+ this.receiverNumber = config.receiverNumber || '';
14
+ // This.siaAccount = config.siaAccount;
15
+ // this.siaAccountPrefix = config.siaAccountPrefix || 'L0';
16
+ this.encryptionKey = config.encryptionKey || '';
17
+ this.encryptionEnabled = this.encryptionKey.length > 0;
18
+
19
+ switch (this.encryptionKey.length) {
20
+ case 32:
21
+ this.encryptionType = 'aes-128-cbc';
22
+ break;
23
+ case 48:
24
+ this.encryptionType = 'aes-192-cbc';
25
+ break;
26
+ case 64:
27
+ this.encryptionType = 'aes-256-cbc';
28
+ break;
29
+ default:
30
+ return undefined;
31
+ }
32
+
33
+
34
+
35
+ const client = new Net.Socket();
36
+
37
+ process.on('uncaughtException', error => {
38
+ console.error('Uncaught Exception:', error);
39
+ client.destroy();
40
+ });
41
+
42
+ this.connect = function () {
43
+ // If(!client.destroyed && client.readyState == 'open') {
44
+ // console.log('Already connected to SIA server at ' + this.receiverHost + ':' + this.receiverPort);
45
+ // return;
46
+ // }
47
+
48
+ if (!this.receiverHost || !this.receiverPort) {
49
+ console.error('Receiver host or port not set');
50
+ return;
51
+ }
52
+
53
+ console.log('Connecting to SIA server at', this.receiverHost, this.receiverPort);
54
+
55
+ const operation = Retry.operation({
56
+ retries: 5,
57
+ factor: 2,
58
+ minTimeout: 1000,
59
+ maxTimeout: 20_000,
60
+ randomize: true,
61
+ });
62
+
63
+ const tmpRef = this;
64
+
65
+ let errorListener = null;
66
+
67
+
68
+ const connectCallback = () => {
69
+ console.log('Connected to SIA server at', tmpRef.receiverHost, tmpRef.receiverPort);
70
+ };
71
+
72
+ operation.attempt(currentAttempt => {
73
+
74
+ if (errorListener) { // will not be active on first attempt
75
+ client.off('error', errorListener);
76
+ client.off('connect', connectCallback);
77
+ }
78
+
79
+ errorListener = error => {
80
+ console.log(`Connection error: ${error.message}`);
81
+ if (operation.retry(error)) {
82
+ console.log(`Retrying connection to SIA server, attempt number: ${currentAttempt}`);
83
+ return;
84
+ }
85
+
86
+ console.error('Failed to connect to SIA server:', error);
87
+ };
88
+
89
+ client.once('error', errorListener);
90
+
91
+ client.connect(tmpRef.receiverPort, tmpRef.receiverHost, connectCallback);
92
+ });
93
+ };
94
+
95
+ this.getSIAConfig = function () {
96
+ return {
97
+ receiverNumber: this.receiverNumber,
98
+ encryptionKey: this.encryptionKey,
99
+ encryptionEnabled: this.encryptionEnabled,
100
+ encryptionType: this.encryptionType,
101
+ };
102
+ };
103
+
104
+ this.write = function (data) {
105
+ if (!client.destroyed && client.readyState == 'open') {
106
+ console.log('Sending data to SIA server:', data);
107
+ client.write(data);
108
+ }
109
+ };
110
+
111
+ this.close = function () {
112
+ if (!client.destroyed) {
113
+ client.end();
114
+ }
115
+ };
116
+
117
+ // Handle sending of data
118
+ // handle failure to connect
119
+ // payload won't be prepared here but in SIA device
120
+
121
+ // client.write('Hello, server.');
122
+ }
123
+
124
+ RED.nodes.registerType('SIA-server', SIAServer);
125
+
126
+ /* istanbul ignore next */
127
+ };
@@ -0,0 +1,280 @@
1
+ AN,ANALOG RESTORAL
2
+ AR,AC RESTORAL
3
+ AS,ANALOG SERVICE
4
+ AT,AC TROUBLE
5
+ BA,BURGLARY ALARM
6
+ BB,BURGLARY BYPASS
7
+ BC,BURGLARY CANCEL
8
+ BD,SWINGER TROUBLE
9
+ BE,SWINGER TRBL RESTORE
10
+ BH,BURG ALARM RESTORE
11
+ BJ,BURG TROUBLE RESTORE
12
+ BM,BURG ALARM CROSS PNT
13
+ BR,BURGLARY RESTORAL
14
+ BS,BURGLARY SUPERVISORY
15
+ BT,BURGLARY TROUBLE
16
+ BU,BURGLARY UNBYPASS
17
+ BV,BURGLARY VERIFIED
18
+ BX,BURGLARY TEST
19
+ BZ,MISSING SUPERVISION
20
+ CA,AUTOMATIC CLOSING
21
+ CD,CLOSING DELINQUENT
22
+ CE,CLOSING EXTEND
23
+ CF,FORCED CLOSING
24
+ CG,CLOSE AREA
25
+ CI,FAIL TO CLOSE
26
+ CJ,LATE CLOSE
27
+ CK,EARLY CLOSE
28
+ CL,CLOSING REPORT
29
+ CM,MISSING AL-RECNT CLS
30
+ CP,AUTOMATIC CLOSING
31
+ CR,RECENT CLOSING
32
+ CS,CLOSE KEY SWITCH
33
+ CT,LATE TO OPEN
34
+ CW,WAS FORCE ARMED
35
+ CZ,POINT CLOSING
36
+ DA,CARD ASSIGNED
37
+ DB,CARD DELETED
38
+ DC,ACCESS CLOSED
39
+ DD,ACCESS DENIED
40
+ DE,REQUEST TO ENTER
41
+ DF,DOOR FORCED
42
+ DG,ACCESS GRANTED
43
+ DH,DOOR LEFT OPEN-RSTRL
44
+ DJ,DOOR FORCED-TROUBLE
45
+ DK,ACCESS LOCKOUT
46
+ DL,DOOR LEFT OPEN-ALARM
47
+ DM,DOOR LEFT OPEN-TRBL
48
+ DN,DOOR LEFT OPEN
49
+ DO,ACCESS OPEN
50
+ DP,ACCESS DENIED-BAD TM
51
+ DQ,ACCESS DENIED-UN ARM
52
+ DR,DOOR RESTORAL
53
+ DS,DOOR STATION
54
+ DT,ACCESS TROUBLE
55
+ DU,DEALER ID#
56
+ DV,ACCESS DENIED-UN ENT
57
+ DW,ACCESS DENIED-INTRLK
58
+ DX,REQUEST TO EXIT
59
+ DY,DOOR LOCKED
60
+ DZ,ACCESS CLOSED STATE
61
+ EA,EXIT ALARM
62
+ EE,EXIT_ERROR
63
+ ER,EXPANSION RESTORAL
64
+ ET,EXPANSION TROUBLE
65
+ EX,EXTRNL DEVICE STATE
66
+ EZ,MISSING ALARM-EXT ER
67
+ FA,FIRE ALARM
68
+ FB,FIRE BYPASS
69
+ FC,FIRE CANCEL
70
+ FH,FIRE ALARM RESTORE
71
+ FI,FIRE TEST BEGIN
72
+ FJ,FIRE TROUBLE RESTORE
73
+ FK,FIRE TEST END
74
+ FM,FIRE ALARM CROSS PNT
75
+ FR,FIRE RESTORAL
76
+ FS,FIRE SUPERVISORY
77
+ FT,FIRE TROUBLE
78
+ FU,FIRE UNBYPASS
79
+ FX,FIRE TEST
80
+ FY,MISSING FIRE TROUBLE
81
+ FZ,MISSING FIRE SPRV
82
+ GA,GAS ALARM
83
+ GB,GAS BYPASS
84
+ GH,GAS ALARM RESTORE
85
+ GJ,GAS TROUBLE RESTORE
86
+ GR,GAS RESTORAL
87
+ GS,GAS SUPERVISORY
88
+ GT,GAS TROUBLE
89
+ GU,GAS UNBYPASS
90
+ GX,GAS TEST
91
+ HA,HOLDUP ALARM
92
+ HB,HOLDUP BYPASS
93
+ HH,HOLDUP ALARM RESTORE
94
+ HJ,HOLDUP TRBL RESTORE
95
+ HR,HOLDUP RESTORAL
96
+ HS,HOLDUP SUPERVISORY
97
+ HT,HOLDUP TROUBLE
98
+ HU,HOLDUP UNBYPASS
99
+ IA,EQPMT FAIL CONDITION
100
+ IR,EQPMT FAIL RESTORE
101
+ JA,USER CODE TAMPER
102
+ JD,DATE CHANGED
103
+ JH,HOLIDAY CHANGED
104
+ JK,LATCHKEY ALERT
105
+ JL,LOG THRESHOLD
106
+ JO,LOG OVERFLOW
107
+ JP,USER ON PREMISES
108
+ JR,SCHEDULE EXECUTED
109
+ JS,SCHEDULE CHANGED
110
+ JT,TIME CHANGED
111
+ JV,USER CODE CHANGED
112
+ JX,USER CODE DELETED
113
+ JY,USER CODE ADDED
114
+ JZ,USER LEVEL SET
115
+ KA,HEAT ALARM
116
+ KB,HEAT BYPASS
117
+ KH,HEAT ALARM RESTORE
118
+ KJ,HEAT TROUBLE RESTORE
119
+ KR,HEAT RESTORAL
120
+ KS,HEAT SUPERVISORY
121
+ KT,HEAT TROUBLE
122
+ KU,HEAT UNBYPASS
123
+ L_,LISTEN IN + SECONDS
124
+ LB,LOCAL PROG. BEGIN
125
+ LD,LOCAL PROG. DENIED
126
+ LE,LISTEN IN ENDED
127
+ LF,LISTEN IN BEGIN
128
+ LR,PHONE LINE RESTORAL
129
+ LS,LOCAL PROG. SUCCESS
130
+ LT,PHONE LINE TROUBLE
131
+ LU,LOCAL PROG. FAIL
132
+ LX,LOCAL PROG. ENDED
133
+ MA,MEDICAL ALARM
134
+ MB,MEDICAL BYPASS
135
+ MH,MEDIC ALARM RESTORE
136
+ MJ,MEDICAL TRBL RESTORE
137
+ MR,MEDICAL RESTORAL
138
+ MS,MEDICAL SUPERVISORY
139
+ MT,MEDICAL TROUBLE
140
+ MU,MEDICAL UNBYPASS
141
+ NA,NO ACTIVITY
142
+ NC,NETWORK CONDITION
143
+ NF,FORCED PERIMETER ARM
144
+ NL,PERIMETER ARMED
145
+ NR,NETWORK RESTORAL
146
+ NS,ACTIVITY RESUMED
147
+ NT,NETWORK FAILURE
148
+ OA,AUTOMATIC OPENING
149
+ OC,CANCEL REPORT
150
+ OG,OPEN AREA
151
+ OH,EARLY TO OPN FROM AL
152
+ OI,FAIL TO OPEN
153
+ OJ,LATE OPEN
154
+ OK,EARLY OPEN
155
+ OL,LATE TO OPEN FROM AL
156
+ OP,OPENING REPORT
157
+ OR,DISARM FROM ALARM
158
+ OS,OPEN KEY SWITCH
159
+ OT,LATE TO CLOSE
160
+ OZ,POINT OPENING
161
+ PA,PANIC ALARM
162
+ PB,PANIC BYPASS
163
+ PH,PANIC ALARM RESTORE
164
+ PJ,PANIC TRBL RESTORE
165
+ PR,PANIC RESTORAL
166
+ PS,PANIC SUPERVISORY
167
+ PT,PANIC TROUBLE
168
+ PU,PANIC UNBYPASS
169
+ QA,EMERGENCY ALARM
170
+ QB,EMERGENCY BYPASS
171
+ QH,EMRGCY ALARM RESTORE
172
+ QJ,EMRGCY TRBL RESTORE
173
+ QR,EMERGENCY RESTORAL
174
+ QS,EMRGCY SUPERVISORY
175
+ QT,EMERGENCY TROUBLE
176
+ QU,EMERGENCY UNBYPASS
177
+ RA,RMOTE PROG CALL FAIL
178
+ RB,REMOTE PROG. BEGIN
179
+ RC,RELAY CLOSE
180
+ RD,REMOTE PROG. DENIED
181
+ RN,REMOTE RESET
182
+ RO,RELAY OPEN
183
+ RP,AUTOMATIC TEST
184
+ RR,RESTORE POWER
185
+ RS,REMOTE PROG. SUCCESS
186
+ RT,DATA LOST
187
+ RU,REMOTE PROG. FAIL
188
+ RX,MANUAL TEST
189
+ RY,TEST OFF NORMAL
190
+ SA,SPRINKLER ALARM
191
+ SB,SPRINKLER BYPASS
192
+ SH,SPRKLR ALARM RESTORE
193
+ SJ,SPRKLR TRBL RESTORE
194
+ SR,SPRINKLER RESTORAL
195
+ SS,SPRINKLER SUPERVISRY
196
+ ST,SPRINKLER TROUBLE
197
+ SU,SPRINKLER UNBYPASS
198
+ TA,TAMPER ALARM
199
+ TB,TAMPER BYPASS
200
+ TC,ALL POINTS TESTED
201
+ TE,TEST END
202
+ TH,TAMPER ALRM RESTORE
203
+ TJ,TAMPER TRBL RESTORE
204
+ TP,WALK TEST POINT
205
+ TR,TAMPER RESTORAL
206
+ TS,TEST START
207
+ TT,TAMPER TROUBLE
208
+ TU,TAMPER UNBYPASS
209
+ TX,TEST REPORT
210
+ UA,UNTYPED ZONE ALARM
211
+ UB,UNTYPED ZONE BYPASS
212
+ UH,UNTYPD ALARM RESTORE
213
+ UJ,UNTYPED TRBL RESTORE
214
+ UR,UNTYPED ZONE RESTORE
215
+ US,UNTYPED ZONE SUPRVRY
216
+ UT,UNTYPED ZONE TROUBLE
217
+ UU,UNTYPED ZONE UNBYPSS
218
+ UX,UNDEFINED ALARM
219
+ UY,UNTYPED MISSING TRBL
220
+ UZ,UNTYPED MISSING ALRM
221
+ VI,PRINTER PAPER IN
222
+ VO,PRINTER PAPER OUT
223
+ VR,PRINTER RESTORE
224
+ VT,PRINTER TROUBLE
225
+ VX,PRINTER TEST
226
+ VY,PRINTER ONLINE
227
+ VZ,PRINTER OFFLINE
228
+ WA,WATER ALARM
229
+ WB,WATER BYPASS
230
+ WH,WATER ALARM RESTORE
231
+ WJ,WATER TRBL RESTORE
232
+ WR,WATER RESTORAL
233
+ WS,WATER SUPERVISORY
234
+ WT,WATER TROUBLE
235
+ WU,WATER UNBYPASS
236
+ XA,EXTRA ACCNT REPORT
237
+ XE,EXTRA POINT
238
+ XF,EXTRA RF POINT
239
+ XH,RF INTERFERENCE RST
240
+ XI,SENSOR RESET
241
+ XJ,RF RCVR TAMPER RST
242
+ XL,LOW RF SIGNAL
243
+ XM,MISSING ALRM-X POINT
244
+ XQ,RF INTERFERENCE
245
+ XR,TRANS. BAT. RESTORAL
246
+ XS,RF RECEIVER TAMPER
247
+ XT,TRANS. BAT. TROUBLE
248
+ XW,FORCED POINT
249
+ XX,FAIL TO TEST
250
+ YA,BELL FAULT
251
+ YB,BUSY SECONDS
252
+ YC,COMMUNICATIONS FAIL
253
+ YD,RCV LINECARD TROUBLE
254
+ YE,RCV LINECARD RESTORE
255
+ YF,PARA CHECKSUM FAIL
256
+ YG,PARAMETER CHANGED
257
+ YH,BELL RESTORED
258
+ YI,OVERCURRENT TROUBLE
259
+ YJ,OVERCURRENT RESTORE
260
+ YK,COMM. RESTORAL
261
+ YM,SYSTEM BATT MISSING
262
+ YN,INVALID REPORT
263
+ YO,UNKNOWN MESSAGE
264
+ YP,PWR SUPPLY TROUBLE
265
+ YQ,PWR SUPPLY RESTORE
266
+ YR,SYSTEM BAT. RESTORAL
267
+ YS,COMMUNICATIONS TRBL
268
+ YT,SYSTEM BAT. TROUBLE
269
+ YW,WATCHDOG RESET
270
+ YX,SERVICE REQUIRED
271
+ YY,STATUS REPORT
272
+ YZ,SERVICE COMPLETED
273
+ ZA,FREEZE ALARM
274
+ ZB,FREEZE BYPASS
275
+ ZH,FREEZE ALARM RESTORE
276
+ ZJ,FREEZE TRBL RESTORE
277
+ ZR,FREEZE RESTORAL
278
+ ZS,FREEZE SUPERVISORY
279
+ ZT,FREEZE TROUBLE
280
+ ZU,FREEZE UNBYPASS