iobroker.openknx 0.1.9 → 0.1.13

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/main.js CHANGED
@@ -11,8 +11,8 @@ const utils = require("@iobroker/adapter-core");
11
11
  const projectImport = require(__dirname + "/lib/projectImport");
12
12
 
13
13
  const knx = require(__dirname + "/lib/knx"); //todo copy for the moment
14
- const _ = require("underscore");
15
14
  const tools = require("./lib/tools.js");
15
+ const DoubleKeyedMap = require("./lib/doubleKeyedMap.js");
16
16
 
17
17
  class openknx extends utils.Adapter {
18
18
  /**
@@ -34,6 +34,28 @@ class openknx extends utils.Adapter {
34
34
  this.on("unload", this.onUnload.bind(this));
35
35
 
36
36
  this.mynamespace = this.namespace;
37
+
38
+ //redirect log from knx.js to adapter log
39
+ console.log = (args) => {
40
+ if (args && typeof args === "string") {
41
+ if (args.indexOf("deferring outbound_TUNNELING_REQUEST") !== -1) {
42
+ return;
43
+ }
44
+ if (args.indexOf("[debug]") !== -1) {
45
+ this.log.debug(args);
46
+ } else if (args.indexOf("[info]") !== -1) {
47
+ this.log.info(args);
48
+ } else if (args.indexOf("[warn]") !== -1) {
49
+ this.log.warn(args);
50
+ } else if (args.indexOf("[error]") !== -1) {
51
+ this.log.error(args);
52
+ }else if (args.indexOf("[trace]") !== -1) {
53
+ this.log.silly(args);
54
+ } else {
55
+ this.log.info(args);
56
+ }
57
+ }
58
+ };
37
59
  }
38
60
 
39
61
  /**
@@ -107,9 +129,22 @@ class openknx extends utils.Adapter {
107
129
  });
108
130
  });
109
131
  break;
132
+ case "createAlias":
133
+ this.log.info("Create aliases...");
134
+ projectImport.findStatusGAs(this, this.gaList, (count) => {
135
+ if (obj.callback) {
136
+ const res = {
137
+ error: null,
138
+ count: count,
139
+ };
140
+ this.sendTo(obj.from, obj.command, res, obj.callback);
141
+ }
142
+ });
143
+ break;
110
144
  case "reset":
111
145
  this.log.info("Restarting...");
112
146
  this.restart();
147
+ // eslint-disable-next-line no-fallthrough
113
148
  default:
114
149
  this.log.warn("Unknown command: " + obj.command);
115
150
  break;
@@ -130,9 +165,8 @@ class openknx extends utils.Adapter {
130
165
  }
131
166
  if (onlyAddNewObjects) {
132
167
  //if user setting Add only new Objects write only new objects
133
- //extend object will overwrite user made element changes if known in the import, not intended
134
- //this.extendObject(this.mynamespace + "." + objects[index]._id, objects[index], (err, obj) => {
135
- this.setObjectNotExists(this.mynamespace + '.' + objects[index]._id, objects[index], (err, obj) => {
168
+ //extend object would overwrite user made element changes if known in the import, not intended
169
+ this.setObjectNotExists(this.mynamespace + "." + objects[index]._id, objects[index], (err, obj) => {
136
170
  if (err) {
137
171
  this.log.warn("error store Object " + objects[index]._id + " " + (err ? " " + err : ""));
138
172
  }
@@ -140,7 +174,7 @@ class openknx extends utils.Adapter {
140
174
  });
141
175
  } else {
142
176
  //setObjet to overwrite all existing settings, default
143
- this.setForeignObject(this.mynamespace + "." + objects[index]._id, objects[index], (err, obj) => {
177
+ this.setObject(this.mynamespace + "." + objects[index]._id, objects[index], (err, obj) => {
144
178
  if (err) {
145
179
  this.log.warn("error store Object " + objects[index]._id + (err ? " " + err : ""));
146
180
  }
@@ -155,7 +189,6 @@ class openknx extends utils.Adapter {
155
189
  warnDuplicates(objects) {
156
190
  const arr = [];
157
191
  const duplicates = [];
158
- let message;
159
192
 
160
193
  for (const object of objects) {
161
194
  arr.push(object._id);
@@ -167,17 +200,17 @@ class openknx extends utils.Adapter {
167
200
  }
168
201
  }
169
202
 
170
- message = "Object with identical Group Address name not created: " + duplicates;
203
+ const message = "Object with an already existing Group Address name has not been created: " + duplicates;
171
204
  if (duplicates.length) {
172
205
  this.log.warn(message);
173
206
  }
174
207
  return duplicates.length ? message : null;
175
208
  }
176
209
 
177
- //obj to string and date to number for iobroker, convert to object for knx
210
+ //obj to string and date to number for iobroker from knx stack
178
211
  convertType(val) {
179
212
  let ret;
180
- //convert, state value for iobroker to set has to be one of type "string", "number", "boolean" and not type "object"
213
+ //convert, state value for iobroker to set has to be one of type "string", "number", "boolean" and additionally type "object"
181
214
  if (val instanceof Date) {
182
215
  //convert Date to number
183
216
  ret = Number(new Date(val));
@@ -185,11 +218,10 @@ class openknx extends utils.Adapter {
185
218
  //before object check
186
219
  ret = val.toString("hex");
187
220
  } else if (typeof val === "object") {
221
+ //https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/objectsschema.md#states
188
222
  ret = JSON.stringify(val);
189
- } else if (typeof val === "string") {
190
- //use as is
191
223
  } else {
192
- //both can handle number and boolean
224
+ //keep sring, boolean and number
193
225
  ret = val;
194
226
  }
195
227
  return ret;
@@ -221,8 +253,18 @@ class openknx extends utils.Adapter {
221
253
  }
222
254
 
223
255
  if (state.ack) {
256
+ //ack flag is responsible to set act flag, only continue when application triggered a change without ack flag
257
+
224
258
  //enable this for system testing
225
259
  //this.interfaceTest(id, state);
260
+
261
+ //check if actGa is available to set the value from status to actGa
262
+ if (this.gaList.getDataById(id).native.actGA) {
263
+ const actGa = this.gaList.getDataById(id).native.actGA;
264
+ const gaId = this.gaList.getIdByAddress(actGa);
265
+ this.log.debug("Set GA " + actGa + " to " + state.val + " from " + id);
266
+ this.setState(gaId, state.val, true);
267
+ }
226
268
  return;
227
269
  }
228
270
 
@@ -231,16 +273,30 @@ class openknx extends utils.Adapter {
231
273
  let knxVal;
232
274
  let rawVal;
233
275
 
276
+ //check for boolean and ensure the correct datatype
277
+ if (this.gaList.getDataById(id).common && this.gaList.getDataById(id).common.type === "boolean") {
278
+ state.val = state.val ? true : false;
279
+ }
280
+ if (this.gaList.getDataById(id).common && this.gaList.getDataById(id).common.type === "number") {
281
+ if (isNaN(Number(state.val))) {
282
+ this.log.warn("Value " +state.val + " for "+ id + " is not a number");
283
+ }
284
+ }
234
285
  //convert val into object for certain dpts
235
286
  if (tools.isDateDPT(dpt)) {
236
287
  //before composite check, date is also composite
237
288
  knxVal = new Date(state.val);
238
289
  } else if (this.gaList.getDataById(id).native.valuetype == "composite") {
239
- try {
240
- knxVal = JSON.parse(state.val);
241
- } catch (e) {
242
- this.log.warn("stateChange: unsupported value format " + state.val + " for " + ga);
243
- return;
290
+ //input from IOB is either object or string in object notation, type of this conversion is object needed by the knx lib
291
+ if (typeof state.val == "object") {
292
+ knxVal = state.val;
293
+ } else {
294
+ try {
295
+ knxVal = JSON.parse(state.val);
296
+ } catch (e) {
297
+ this.log.warn("stateChange: unsupported value format " + state.val + " for " + ga);
298
+ return;
299
+ }
244
300
  }
245
301
  } else if (tools.isStringDPT(dpt)) {
246
302
  knxVal = state.val;
@@ -249,17 +305,17 @@ class openknx extends utils.Adapter {
249
305
  //bitlength is the buffers bytelength * 8.
250
306
  rawVal = Buffer.from(state.val, "hex");
251
307
  isRaw = true;
252
- this.log.warn("Missing implementation for unhandeled DPT " + dpt + ", assuming raw values");
308
+ this.log.debug("Unhandeled DPT " + dpt + ", assuming raw value");
253
309
  } else {
254
310
  knxVal = state.val;
255
311
  }
256
312
 
257
313
  if (state.c == "GroupValue_Read") {
258
314
  //interface to trigger GrouValue_Read is this comment
259
- this.log.debug("Outgoing GroupValue_Read to " + ga + " value " + knxVal);
315
+ this.log.debug("Outbound GroupValue_Read to " + ga + " value " + JSON.stringify(knxVal));
260
316
  this.knxConnection.read(ga);
261
317
  } else if (this.gaList.getDataById(id).common.write) {
262
- this.log.debug("Outgoing GroupValue_Write to " + ga + " value " + (isRaw ? rawVal : knxVal) + " from " + id);
318
+ this.log.debug("Outbound GroupValue_Write to " + ga + " value " + (isRaw ? rawVal : JSON.stringify(knxVal)) + " from " + id);
263
319
  if (isRaw) {
264
320
  this.knxConnection.writeRaw(ga, rawVal);
265
321
  } else {
@@ -293,21 +349,18 @@ class openknx extends utils.Adapter {
293
349
  if (this.gaList.getDataById(key).native.address.match(/\d*\/\d*\/\d*/) && this.gaList.getDataById(key).native.dpt) {
294
350
  try {
295
351
  const dp = new knx.Datapoint({
296
- ga: this.gaList.getDataById(key).native.address,
297
- dpt: this.gaList.getDataById(key).native.dpt,
298
- autoread: this.gaList.getDataById(key).native.autoread, // issue a GroupValue_Read request to try to get the initial state from the bus (if any)
299
- },
300
- this.knxConnection
352
+ ga: this.gaList.getDataById(key).native.address,
353
+ dpt: this.gaList.getDataById(key).native.dpt,
354
+ autoread: this.gaList.getDataById(key).native.autoread, // issue a GroupValue_Read request to try to get the initial state from the bus (if any)
355
+ },
356
+ this.knxConnection
301
357
  );
302
358
  this.gaList.setDpById(key, dp);
303
359
  cnt_withDPT++;
304
360
  this.log.debug(
305
- "Datapoint " +
306
- (this.gaList.getDataById(key).native.autoread ? "autoread " : "") +
307
- "created and GroupValueWrite sent: " +
308
- this.gaList.getDataById(key).native.address +
309
- " " +
310
- key
361
+ `Datapoint ${this.gaList.getDataById(key).native.autoread ? "autoread " : ""} created and GroupValueWrite sent: ${
362
+ this.gaList.getDataById(key).native.address
363
+ } ${key}`
311
364
  );
312
365
  } catch (e) {
313
366
  this.log.warn("could not create KNX Datapoint for " + key + " with error: " + e);
@@ -333,7 +386,11 @@ class openknx extends utils.Adapter {
333
386
  },
334
387
 
335
388
  //KNX Bus event received
336
- event: ( /** @type {string} */ evt, /** @type {string} */ src, /** @type {string} */ dest, /** @type {string} */ val) => {
389
+
390
+ //src: KnxDeviceAddress, dest: KnxGroupAddress
391
+ event: (/** @type {string} */ evt, /** @type {string} */ src, /** @type {string} */ dest, /** @type {string} */ val) => {
392
+ let convertedVal;
393
+
337
394
  if (src == this.config.eibadr) {
338
395
  //called by self, avoid loop
339
396
  //console.log('receive self ga: ', dest);
@@ -341,12 +398,15 @@ class openknx extends utils.Adapter {
341
398
  }
342
399
 
343
400
  /* some checks */
401
+ if (dest == "0/0/0" || tools.isDeviceAddress(dest)) {
402
+ //seems that knx lib does not guarantee dest group adresses
403
+ return;
404
+ }
344
405
  if (!this.gaList.getDpByAddress(dest)) {
345
- this.log.warn("Ignoring " + evt + " received on unknown GA: " + dest);
406
+ this.log.warn("Ignoring " + evt + " received on unknown GA: " + dest + ". GA was not in imported XML");
346
407
  return;
347
408
  }
348
409
 
349
- let convertedVal;
350
410
  if (tools.isStringDPT(this.gaList.getDataByAddress(dest).native.dpt)) {
351
411
  convertedVal = this.gaList.getDpByAddress(dest).current_value;
352
412
  } else {
@@ -356,13 +416,11 @@ class openknx extends utils.Adapter {
356
416
  switch (evt) {
357
417
  case "GroupValue_Read":
358
418
  //fetch val from addressed object and write on bus if configured to answer
359
- this.getForeignState(this.gaList.getIdByAddress(dest), (err, state) => {
419
+ this.getState(this.gaList.getIdByAddress(dest), (err, state) => {
360
420
  if (state) {
361
- this.log.debug("Incoming GroupValue_Read from " + src + " to " + "(" + dest + ") " + this.gaList.getDataByAddress(dest).common.name);
421
+ this.log.debug("Inbound GroupValue_Read from " + src + " to " + "(" + dest + ") " + this.gaList.getDataByAddress(dest).common.name);
362
422
  if (this.gaList.getDataByAddress(dest).native.answer_groupValueResponse) {
363
- //https://bitbucket.org/ekarak/knx.js/issues/83/send-groupvalue_response
364
- //workaround, send out a write instead response
365
- this.knxConnection.write(dest, state.val, this.gaList.getDataByAddress(dest).native.dpt);
423
+ this.knxConnection.respond(dest, state.val, this.gaList.getDataByAddress(dest).native.dpt);
366
424
  this.log.debug("responding with value " + state.val);
367
425
  }
368
426
  }
@@ -374,7 +432,7 @@ class openknx extends utils.Adapter {
374
432
  val: convertedVal,
375
433
  ack: true,
376
434
  });
377
- this.log.debug("Incoming GroupValue_Response from " + src + " to " + "(" + dest + ") " + this.gaList.getDataByAddress(dest).common.name + ": " + convertedVal);
435
+ this.log.debug(`Inbound GroupValue_Response from ${src} to (${dest}) ${this.gaList.getDataByAddress(dest).common.name} : ${convertedVal}`);
378
436
  break;
379
437
 
380
438
  case "GroupValue_Write":
@@ -383,15 +441,9 @@ class openknx extends utils.Adapter {
383
441
  ack: true,
384
442
  });
385
443
  this.log.debug(
386
- "Incoming GroupValue_Write ga: " +
387
- dest +
388
- " val: " +
389
- convertedVal +
390
- " dpt: " +
391
- this.gaList.getDataByAddress(dest).native.dpt +
392
- " to Object: " +
393
- this.gaList.getIdByAddress(dest)
444
+ `Inbound GroupValue_Write ${dest} val: ${convertedVal} dpt: ${this.gaList.getDataByAddress(dest).native.dpt} to Object: ${this.gaList.getIdByAddress(dest)}`
394
445
  );
446
+
395
447
  break;
396
448
 
397
449
  default:
@@ -412,7 +464,7 @@ class openknx extends utils.Adapter {
412
464
  const out = outpath + id.replace(inpath, "");
413
465
  this.setState(out, {
414
466
  val: state.val,
415
- ack: true,
467
+ ack: false,
416
468
  });
417
469
  }
418
470
  }
@@ -437,7 +489,7 @@ class openknx extends utils.Adapter {
437
489
  for (let i = res.rows.length - 1; i >= 0; i--) {
438
490
  const id = res.rows[i].id;
439
491
  const value = res.rows[i].value;
440
- if (value && value.native.address != undefined) {
492
+ if (value && value.native && value.native.address != undefined) {
441
493
  //add only elements from tree that are knx objects, identified by a group adress
442
494
  this.gaList.set(id, value.native.address, res.rows[i].value);
443
495
  }
@@ -449,59 +501,6 @@ class openknx extends utils.Adapter {
449
501
  }
450
502
  }
451
503
 
452
- class DoubleKeyedMap {
453
- constructor() {
454
- //id, ga
455
- this.keymap = new Map();
456
- //id, iobroker object
457
- this.data = new Map();
458
- //id, knx dp
459
- this.dp = new Map();
460
- }
461
- //update or add
462
- set(id, address, data) {
463
- this.keymap.set(address, id);
464
- this.data.set(id, data);
465
- }
466
- //only dp returns transformed value, hold a reference to it
467
- setDpById(id, dp) {
468
- this.dp.set(id, dp);
469
- }
470
- getDpById(id) {
471
- return this.dp.get(id);
472
- }
473
- getDpByAddress(address) {
474
- return this.dp.get(this.keymap.get(address));
475
- }
476
- getDataById(id) {
477
- return this.data.get(id);
478
- }
479
- getDataByAddress(address) {
480
- return this.data.get(this.keymap.get(address));
481
- }
482
- getIdByAddress(address) {
483
- return this.keymap.get(address);
484
- }
485
-
486
- //key value is id
487
- [Symbol.iterator] = () => {
488
- return {
489
- index: -1,
490
- data: this.data,
491
- next() {
492
- return ++this.index < this.data.size ?
493
- {
494
- done: false,
495
- value: Array.from(this.data.keys())[this.index],
496
- } :
497
- {
498
- done: true,
499
- };
500
- },
501
- };
502
- };
503
- }
504
-
505
504
  if (require.main !== module) {
506
505
  // Export the constructor in compact mode
507
506
  /**
@@ -511,4 +510,4 @@ if (require.main !== module) {
511
510
  } else {
512
511
  // otherwise start the instance directly
513
512
  new openknx();
514
- }
513
+ }
package/package.json CHANGED
@@ -1,37 +1,26 @@
1
1
  {
2
2
  "name": "iobroker.openknx",
3
- "version": "0.1.9",
3
+ "version": "0.1.13",
4
4
  "description": "ioBroker knx Adapter",
5
5
  "author": "boellner",
6
6
  "homepage": "https://github.com/iobroker-community-adapters/ioBroker.openknx.git",
7
7
  "license": "GPL-3.0-only",
8
8
  "keywords": [
9
9
  "ioBroker",
10
- "knx",
11
- "Smart Home",
12
- "home automation"
10
+ "knx", "eib", "ets",
11
+ "bus", "communication",
12
+ "Smart Home", "home automation", "Heimautomatisierung"
13
13
  ],
14
14
  "repository": {
15
15
  "type": "git",
16
16
  "url": "https://github.com/iobroker-community-adapters/ioBroker.openknx"
17
17
  },
18
- "dependencies_unused": {
19
- "binary-parser": "^1.1.5",
20
- "binary-protocol": "^0.0.0",
21
- "fs": "^0.0.2",
22
- "ipaddr.js": "1.2.0",
23
- "ipv4.js": "^0.0.2",
24
- "machina": "^2.0.0-1",
25
- "similarity": "^1.1.0",
26
- "xml2js": "^0.4.17",
27
- "xml2js-xpath": "^0.7.0"
28
- },
29
18
  "dependencies": {
30
19
  "@iobroker/adapter-core": "^2.5.1",
31
20
  "knx": "2.4.1",
32
- "underscore": "^1.8.3",
33
- "xmldom": "^0.1.27",
34
- "xpath": "0.0.23"
21
+ "xmldom": "^0.6.0",
22
+ "xpath": "0.0.32",
23
+ "similarity": "^1.2.1"
35
24
  },
36
25
  "devDependencies": {
37
26
  "@alcalzone/release-script": "^2.2.1",
@@ -69,9 +58,8 @@
69
58
  "test:js": "mocha --config test/mocharc.custom.json \"{!(node_modules|test)/**/*.test.js,*.test.js,test/**/test!(PackageFiles|Startup).js}\"",
70
59
  "test:package": "mocha test/package --exit",
71
60
  "test:unit": "mocha test/unit --exit",
72
- "test:integration": "mocha test/integration --exit",
61
+ "test:integration": "mocha test/integration --exit --timeout 15000",
73
62
  "test": "npm run test:js && npm run test:package",
74
- "test:myunittest": "mocha test/test --exit",
75
63
  "check": "tsc --noEmit -p tsconfig.check.json",
76
64
  "lint": "eslint",
77
65
  "release": "release-script"
Binary file