iobroker.openknx 0.1.24 → 0.2.2

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 CHANGED
@@ -126,9 +126,9 @@ The whole name including path is used to check for similarity.
126
126
 
127
127
  ### ACK flags
128
128
  Applications shall never set ack flags, application is notified from this adapter by the ack flag if data is updated.
129
- KNX Stack sets the ack flag of the corresponding IoBroker object on receiption of a group address.
130
- Sent frames on KNX triggered by application writing to a object does not result into an acknowledgement message to that object.
131
- This behavior can be overwritten by checking in the admin dialog the checkbox "set acknowledgement flag when application writes to object".
129
+ KNX Stack sets the ack flag of the corresponding IoBroker object on receiption of a group address if another knx host writes to the bus.
130
+ Sent frames on KNX triggered by application writing to a object does not result into an immediate acknowledgement message to that object.
131
+ If the write is coming from this adapter, the the ack flag is generated on postivive confirmance in tunneling mode.
132
132
 
133
133
  ### Node Red complex datatype example
134
134
  Create a function node that connects to a ioBroker out node that connects with a KNX object of DPT2.
@@ -306,6 +306,18 @@ Data is sent to Iobroker Sentry server hosted in Germany. If you have allowed io
306
306
  - only IPv4 supported
307
307
 
308
308
  ## Changelog
309
+ ### 0.2.2 (2022-05-26)
310
+ * feature: writing to bus l_data.con creates a ack on the iobroker object if successful (the knx conf flag unset)
311
+ * bugfix: remove manual Physical KNX address dialog, use 0.0.0 instead
312
+ * bugfix: remove error log when answering to GroupValueRead: #183
313
+ * bugfix: improve warning logs on intended and unintended disconnects
314
+
315
+ ### 0.1.25 (2022-04-18)
316
+ * feature: datatype check for raw value
317
+ * feature: check if knx is connected before usage
318
+ * bugfix: if update ack after write, use correct timestamp and set adapter as user
319
+ * bugfix: remove enless loop if event received before initialisation
320
+
309
321
  ### 0.1.24 (2022-03-31)
310
322
  * feature: support for latin1 charset in dpt16
311
323
 
@@ -433,7 +445,7 @@ Data is sent to Iobroker Sentry server hosted in Germany. If you have allowed io
433
445
  * (boellner) feature: import ga xml
434
446
 
435
447
  ### initial version
436
- * initial version inspired by https://www.npmjs.com/package/iobroker.knx/v/0.8.3
448
+ * initial version from https://www.npmjs.com/package/iobroker.knx/v/0.8.3
437
449
 
438
450
  ## License
439
451
  GNU GENERAL PUBLIC LICENSE
@@ -78,7 +78,6 @@
78
78
  if (settings.gwip === undefined) settings.gwip = "127.0.0.1";
79
79
  if (settings.gwipport === undefined) settings.gwipport = 3671;
80
80
  if (settings.minimumDelay === undefined) settings.minimumDelay = 50;
81
- if (settings.eibadr === undefined) settings.eibadr = "1.1.1";
82
81
  if (settings.onlyAddNewObjects === undefined) settings.onlyAddNewObjects = false;
83
82
  if (settings.localInterface === undefined) settings.localInterface = "";
84
83
  if (settings.setAckOnWrite === undefined) settings.setAckOnWrite = false;
@@ -136,16 +135,6 @@
136
135
  }
137
136
  }
138
137
 
139
- if (id === "eibadr") {
140
- var eibadr1 = $("#eibadr").val();
141
- if (isKnxAddressValid(eibadr1)) {
142
- $("#eibadr").val(eibadr1);
143
- } else {
144
- alert("physical KNX address in format a/b/c not valid");
145
- $("#eibadr").val(settings.eibadr).change();
146
- }
147
- }
148
-
149
138
  if (id === "minimumDelay") {
150
139
  var intervall = $("#minimumDelay").val();
151
140
  number = Number(intervall);
@@ -280,7 +269,6 @@
280
269
  } else {
281
270
  $("#deviceName")[0].innerHTML = result.deviceName;
282
271
  $("#gwip").val(result.ip).change();
283
- $("#eibadr").val(result.knxAdr).change();
284
272
  $("#gwipport").val(result.port).change();
285
273
  if (result.devicesFound > 1) showMessage(_("more than one KNX gateway found"));
286
274
  }
@@ -357,11 +345,6 @@
357
345
  <input type="text" class="value" id="gwipport" />
358
346
  <label for="gwipport" class="translate">Port</label>
359
347
  </div>
360
-
361
- <div class="col s6 l3 input-field">
362
- <input class="value" id="eibadr" type="text" />
363
- <label for="eibadr" class="translate">Physical KNX address</label>
364
- </div>
365
348
  <div class="col s6 l2">
366
349
  <a id="detectInterface" class="waves-effect waves-light btn"><i
367
350
  class="material-icons right">live help</i><span class="translate">detect
@@ -382,13 +365,6 @@
382
365
  <label for="minimumDelay" class="translate">Frames delay [ms]</label>
383
366
  </div>
384
367
  </div>
385
- <div class="row">
386
- <div class="col s12 input-field">
387
- <input class="value" id="setAckOnWrite" type="checkbox" />
388
- <label class="translate" for="setAckOnWrite">set acknowledgement flag when application writes to object</label>
389
- </div>
390
-
391
- </div>
392
368
  </div>
393
369
  </div>
394
370
  <div class="card">
package/admin/openknx.png CHANGED
Binary file
package/io-package.json CHANGED
@@ -1,19 +1,11 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "openknx",
4
- "version": "0.1.24",
4
+ "version": "0.2.2",
5
5
  "news": {
6
- "0.1.24": {
7
- "en": "Bugfixing",
8
- "de": "Fehler beheben",
9
- "ru": "Исправление ошибок",
10
- "pt": "Corrigindo erro",
11
- "nl": "Bugfixing",
12
- "fr": "Correction de bogues",
13
- "it": "Correzione di bug",
14
- "es": "Corrección de errores",
15
- "pl": "Naprawa błędów",
16
- "zh-cn": "错误修复"
6
+ "0.2.2": {
7
+ "en": "see https://www.npmjs.com/package/iobroker.openknx",
8
+ "de": "https://www.npmjs.com/package/iobroker.openknx"
17
9
  }
18
10
  },
19
11
  "title": "Open KNX",
@@ -74,7 +74,7 @@ FSM.prototype.AddCEMI = function(datagram, msgcode) {
74
74
  ctrl: {
75
75
  frameType: 1, // 0=extended 1=standard
76
76
  reserved: 0, // always 0
77
- repeat: 1, // the OPPOSITE: 1=do NOT repeat
77
+ repeat: 1, // 0: no repetition, 1=don't care
78
78
  broadcast: 1, // 0-system broadcast 1-broadcast
79
79
  priority: 3, // 0-system 1-normal 2-urgent 3-low
80
80
  acknowledge: sendAck ? 1 : 0,
@@ -348,18 +348,18 @@ module.exports = machina.Fsm.extend({
348
348
  */
349
349
  ['inbound_TUNNELING_REQUEST_L_Data.con'](datagram) {
350
350
  if (this.useTunneling) {
351
- const confirmed = this.sentTunnRequests[datagram.cemi.dest_addr];
352
- if (confirmed) {
353
- delete this.sentTunnRequests[datagram.cemi.dest_addr];
354
- this.emit('confirmed', confirmed);
355
- }
351
+ const confirmed = this.sentTunnRequests[datagram.cemi.dest_addr] && ! /*cofirmation error*/ datagram.cemi.ctrl.confirm;
352
+
353
+ delete this.sentTunnRequests[datagram.cemi.dest_addr];
354
+ this.emit('confirmed', datagram.cemi.dest_addr, confirmed);
355
+
356
356
  KnxLog.get().trace(
357
357
  '(%s): %s %s',
358
358
  this.compositeState(),
359
359
  datagram.cemi.dest_addr,
360
360
  confirmed
361
361
  ? 'delivery confirmation (L_Data.con) received'
362
- : 'unknown dest addr'
362
+ : 'confirmation error'
363
363
  );
364
364
  this.acknowledge(datagram);
365
365
  }
@@ -558,6 +558,7 @@ module.exports = machina.Fsm.extend({
558
558
  },
559
559
  },
560
560
 
561
+ /* send tunnel acknowledge on knx bus*/
561
562
  acknowledge(datagram) {
562
563
  const ack = this.prepareDatagram(
563
564
  KnxConstants.SERVICE_TYPE.TUNNELING_ACK,
package/main.js CHANGED
@@ -30,9 +30,6 @@ class openknx extends utils.Adapter {
30
30
  });
31
31
  this.gaList = new DoubleKeyedMap();
32
32
  this.autoreaddone = false;
33
- /* knx stack starts connection process with disconnect msg*/
34
- this.disconnectConfirmed = false;
35
- this.connected = false;
36
33
  this.on("ready", this.onReady.bind(this));
37
34
  this.on("stateChange", this.onStateChange.bind(this));
38
35
  this.on("message", this.onMessage.bind(this));
@@ -51,7 +48,7 @@ class openknx extends utils.Adapter {
51
48
  if (args.indexOf("deferring outbound_TUNNELING_REQUEST") !== -1) {
52
49
  return;
53
50
  } else if (args.indexOf("empty internal fsm queue due to inbound_DISCONNECT_REQUEST") !== -1) {
54
- this.log.warn("possible data loss due to gateway reset, consider increasing frame delay");
51
+ //this.log.warn("possible data loss due to gateway reset, consider increasing frame delay");
55
52
  }
56
53
 
57
54
  if (args.indexOf("[debug]") !== -1) {
@@ -62,7 +59,13 @@ class openknx extends utils.Adapter {
62
59
  this.log.warn(args);
63
60
  } else if (args.indexOf("[error]") !== -1) {
64
61
  this.log.error(args);
65
- //todo log onsentry
62
+ if (this.sentryInstance) {
63
+ this.Sentry && this.Sentry.withScope(scope => {
64
+ scope.setLevel("error");
65
+ scope.setExtra("error message", args);
66
+ this.Sentry.captureMessage("knx library error event", "error");
67
+ });
68
+ }
66
69
  } else if (args.indexOf("[trace]") !== -1) {
67
70
  this.log.silly(args);
68
71
  } else {
@@ -78,40 +81,18 @@ class openknx extends utils.Adapter {
78
81
  */
79
82
  async onReady() {
80
83
  // adapter initialization
81
-
82
- /*
83
84
  if (this.supportsFeature && this.supportsFeature("PLUGINS")) {
84
85
  const sentryInstance = this.getPluginInstance("sentry");
85
86
  if (sentryInstance) {
86
87
  const Sentry = sentryInstance.getSentryObject();
87
88
  if (Sentry) {
88
89
  Sentry.init({
89
- //environment: "development", //"production", todo distinguish
90
+ //environment: "development", //"production"
91
+ environment: "production"
90
92
  });
91
- Sentry.configureScope(scope => {
92
- scope.addEventProcessor((event, _hint) => {
93
- if (event.exception && event.exception.values && event.exception.values[0]) {
94
- const eventData = event.exception.values[0];
95
- if (eventData.stacktrace && eventData.stacktrace.frames && Array.isArray(eventData.stacktrace.frames) && eventData.stacktrace.frames.length) {
96
- /*
97
- //Exclude event if own directory is included but not inside own node_modules
98
- const ownNodeModulesDir = nodePath.join(__dirname, "node_modules");
99
- if (!eventData.stacktrace.frames.find(frame => frame.filename && frame.filename.includes(__dirname) && !frame.filename.includes(ownNodeModulesDir))) {
100
- return null;
101
- }
102
- */
103
- // We have exception data and do not sorted it out, so report it
104
- // return event;
105
- // }
106
- //}
107
- // No exception in it ... do not report
108
- // return null;
109
- // });
110
- // });
111
- // }
112
- // }
113
- //}
114
-
93
+ }
94
+ }
95
+ }
115
96
 
116
97
  //after installation
117
98
  if (tools.isEmptyObject(this.config)) {
@@ -140,8 +121,7 @@ class openknx extends utils.Adapter {
140
121
  // clearTimeout(timeout2);
141
122
  // ...
142
123
  // clearInterval(interval1);
143
-
144
- this.disconnectConfirmed = false;
124
+ this.startup = true;
145
125
  if (this.knxConnection) {
146
126
  this.knxConnection.Disconnect();
147
127
  }
@@ -315,7 +295,7 @@ class openknx extends utils.Adapter {
315
295
 
316
296
  /**
317
297
  * Is called if a subscribed state changes
318
- * state.ack is coming in false if set by user (nodered, script...), here we set it.
298
+ * state.ack is received with value false if set by user (nodered, script...), here we set it.
319
299
  * https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/adapterdev.md
320
300
  * @param {string} id
321
301
  * @param {ioBroker.State | null | undefined} state
@@ -329,12 +309,15 @@ class openknx extends utils.Adapter {
329
309
  if (!this.gaList.getDataById(id) || !this.gaList.getDataById(id).native || !this.gaList.getDataById(id).native.address) {
330
310
  return "not a KNX object";
331
311
  }
312
+ if (this.knxConnection == undefined) {
313
+ return "KNX not started";
314
+ }
315
+
332
316
  if (state.ack) {
333
317
  //only continue when application triggered a change without ack flag, filter out reception state changes
334
318
 
335
319
  //enable this for system testing
336
320
  //this.interfaceTest(id, state);
337
-
338
321
  return "ack is set";
339
322
  }
340
323
  if (!(await this.getStateAsync("info.connection"))) {
@@ -375,6 +358,10 @@ class openknx extends utils.Adapter {
375
358
  } else if (tools.isUnknownDPT(dpt)) {
376
359
  //write raw buffers for unknown dpts, iterface is a hex value
377
360
  //bitlength is the buffers bytelength * 8.
361
+ if (typeof (knxVal) != "string") {
362
+ this.log.warn("unsupported datatype for raw value");
363
+ return "unsupported datatype";
364
+ }
378
365
  rawVal = Buffer.from(knxVal, "hex");
379
366
  isRaw = true;
380
367
  this.log.info("Unhandeled DPT " + dpt + ", assuming raw value");
@@ -386,6 +373,7 @@ class openknx extends utils.Adapter {
386
373
  }
387
374
  }
388
375
 
376
+ // @ts-ignore
389
377
  if (state.c == "GroupValue_Read" || state.q == 0x10) {
390
378
  //interface to trigger GrouValue_Read is this comment or null
391
379
  this.log.debug("Outbound GroupValue_Read to " + ga);
@@ -402,14 +390,10 @@ class openknx extends utils.Adapter {
402
390
  this.log.debug("Outbound GroupValue_Write to " + ga + " val: " + (isRaw ? rawVal : JSON.stringify(knxVal)) + " from " + id);
403
391
  if (isRaw) {
404
392
  this.knxConnection.writeRaw(ga, rawVal, () => {
405
- if (this.config.setAckOnWrite)
406
- this.setState(id, state, true);
407
393
  });
408
394
  return "write raw";
409
395
  } else {
410
396
  this.knxConnection.write(ga, knxVal, dpt, () => {
411
- if (this.config.setAckOnWrite)
412
- this.setState(id, state, true);
413
397
  });
414
398
  return "write";
415
399
  }
@@ -420,10 +404,11 @@ class openknx extends utils.Adapter {
420
404
  }
421
405
 
422
406
  startKnxStack() {
407
+ this.startup = true;
423
408
  this.knxConnection = this.knx.Connection({
424
409
  ipAddr: this.config.gwip,
425
410
  ipPort: this.config.gwipport,
426
- physAddr: this.config.eibadr,
411
+ physAddr: "0.0.0",
427
412
  interface: this.translateInterface(this.config.localInterface),
428
413
  minimumDelay: this.config.minimumDelay,
429
414
  //https://github.com/Supergiovane/node-red-contrib-knx-ultimate/issues/78, some receivers cannot handle a ack request, spec makes no difference
@@ -434,8 +419,6 @@ class openknx extends utils.Adapter {
434
419
  //debug:
435
420
  handlers: {
436
421
  connected: () => {
437
- this.disconnectConfirmed = false;
438
- this.connected = true;
439
422
  //create new knx datapoint and bind to connection
440
423
  //in order to have autoread work
441
424
  let cnt_withDPT = 0;
@@ -444,11 +427,11 @@ class openknx extends utils.Adapter {
444
427
  for (const key of this.gaList) {
445
428
  try {
446
429
  const datapoint = new this.knx.Datapoint({
447
- ga: this.gaList.getDataById(key).native.address,
448
- dpt: this.gaList.getDataById(key).native.dpt,
449
- autoread: this.gaList.getDataById(key).native.autoread, // issue a GroupValue_Read request to try to get the initial state from the bus (if any)
450
- },
451
- this.knxConnection
430
+ ga: this.gaList.getDataById(key).native.address,
431
+ dpt: this.gaList.getDataById(key).native.dpt,
432
+ autoread: this.gaList.getDataById(key).native.autoread, // issue a GroupValue_Read request to try to get the initial state from the bus (if any)
433
+ },
434
+ this.knxConnection
452
435
  );
453
436
  datapoint.on("error", (ga, dptid) => {
454
437
  this.log.warn("Received data length for GA " + ga + " does not match configured " + dptid);
@@ -456,7 +439,7 @@ class openknx extends utils.Adapter {
456
439
  this.gaList.setDpById(key, datapoint);
457
440
  cnt_withDPT++;
458
441
  this.log.debug(
459
- `Datapoint ${this.gaList.getDataById(key).native.autoread ? "autoread" : ""} created and GroupValueWrite sent: ${
442
+ `Datapoint ${this.gaList.getDataById(key).native.autoread ? "autoread " : ""}created and GroupValueWrite sent: ${
460
443
  this.gaList.getDataById(key).native.address
461
444
  } ${key}`
462
445
  );
@@ -473,11 +456,28 @@ class openknx extends utils.Adapter {
473
456
 
474
457
  disconnected: () => {
475
458
  this.setState("info.connection", false, true);
476
- if (this.disconnectConfirmed) {
459
+ if (this.startup != true) {
460
+ //do not warn on initial startup or shutdown
477
461
  this.log.warn("Connection lost");
478
462
  }
479
- this.disconnectConfirmed = true;
480
- this.connected = false;
463
+ this.startup = false;
464
+ },
465
+ error: (connstatus) => {
466
+ this.log.warn(connstatus);
467
+ },
468
+
469
+ //l_data.con, confirmation set bei receiver with set ga s flag
470
+ confirmed: (dest, confirmed) => {
471
+ for (const id of this.gaList.getIdsByGa(dest)) {
472
+ if (confirmed) //if unconfirmed keep ack at false
473
+ this.setState(id, {
474
+ ack: true,
475
+ });
476
+ if (confirmed)
477
+ this.log.debug(`confirmation true received for ${dest}`);
478
+ else
479
+ this.log.info(`confirmation false received for ${dest} ${id}`);
480
+ }
481
481
  },
482
482
 
483
483
  //KNX Bus event received
@@ -486,26 +486,21 @@ class openknx extends utils.Adapter {
486
486
  event: ( /** @type {string} */ evt, /** @type {string} */ src, /** @type {string} */ dest, /** @type {string} */ val) => {
487
487
  let convertedVal = [];
488
488
  let ret = "unknown";
489
- //workaround, lib can fire event without going through connected state?
490
- if (!this.connected) {
491
- this.knxConnection.Disconnect();
492
- this.startKnxStack();
493
- const err ="event received before initialisation";
494
- this.log.warn(err);
495
- return err;
496
- }
489
+
490
+ this.log.silly(`event ${evt} src: ${src} dst: ${dest}`);
497
491
 
498
492
  if (src == this.config.eibadr) {
499
- //called by self, avoid loop
493
+ //L_data.ind of own L_data.req
500
494
  return "receive self ga";
501
495
  }
496
+
502
497
  /* some checks */
503
498
  if (dest == "0/0/0" || tools.isDeviceAddress(dest)) {
504
499
  //seems that knx lib does not guarantee dest group adresses
505
500
  return "bad address";
506
501
  }
507
502
  if (!this.gaList.getIdsByGa(dest)) {
508
- this.log.warn("Ignoring " + evt + " received unknown GA: " + dest);
503
+ this.log.warn(`Ignoring ${evt} received unknown GA: ${dest}`);
509
504
  return "unknown GA";
510
505
  }
511
506
 
@@ -525,7 +520,7 @@ class openknx extends utils.Adapter {
525
520
  this.getState(id, (err, state) => {
526
521
  let ret;
527
522
  if (state) {
528
- this.log.debug("Inbound GroupValue_Read from " + src + " GA " + dest + " to " + id);
523
+ this.log.debug(`Inbound GroupValue_Read from ${src} GA ${dest} to ${id}`);
529
524
  ret = "GroupValue_Read";
530
525
  if (this.gaList.getDataById(id).native.answer_groupValueResponse) {
531
526
  let stateval = state.val;
@@ -534,7 +529,7 @@ class openknx extends utils.Adapter {
534
529
  stateval = JSON.parse(state.val);
535
530
  } catch (e) {}
536
531
  this.knxConnection.respond(dest, stateval, this.gaList.getDataById(id).native.dpt);
537
- this.log.error("responding with value " + state.val);
532
+ this.log.debug("responding with value " + state.val);
538
533
  ret = "GroupValue_Read Respond";
539
534
  }
540
535
  return ret;
@@ -641,16 +636,16 @@ class openknx extends utils.Adapter {
641
636
  }
642
637
  }
643
638
  if (startKnxConnection)
644
- try {
645
- this.startKnxStack();
646
- } catch (e) {
647
- if (e.toString().indexOf("not found or has no useful IPv4 address!") !== -1)
648
- //ipaddr: the address has neither IPv6 nor IPv4 format ??
649
- //only handle certain exceptions
650
- this.log.error(`Cannot start KNX Stack ${e}`);
651
- else
652
- throw e;
653
- }
639
+ try {
640
+ this.startKnxStack();
641
+ } catch (e) {
642
+ if (e.toString().indexOf("not found or has no useful IPv4 address!") !== -1)
643
+ //ipaddr: the address has neither IPv6 nor IPv4 format ??
644
+ //only handle certain exceptions
645
+ this.log.error(`Cannot start KNX Stack ${e}`);
646
+ else
647
+ throw e;
648
+ }
654
649
  }
655
650
  }
656
651
  );
@@ -673,4 +668,4 @@ if (require.main !== module) {
673
668
 
674
669
  // otherwise start the instance directly
675
670
  new openknx();
676
- }
671
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.openknx",
3
- "version": "0.1.24",
3
+ "version": "0.2.2",
4
4
  "description": "ioBroker knx Adapter",
5
5
  "author": "boellner",
6
6
  "homepage": "https://github.com/iobroker-community-adapters/ioBroker.openknx.git",
@@ -29,7 +29,6 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@iobroker/adapter-core": "^2.6.0",
32
- "js-controller": "0.0.6",
33
32
  "knx": "2.4.1",
34
33
  "similarity": "^1.2.1",
35
34
  "xmldom": "^0.6.0",
@@ -37,31 +36,37 @@
37
36
  },
38
37
  "devDependencies": {
39
38
  "@alcalzone/release-script": "^3.5.6",
40
- "@iobroker/testing": "^2.5.6",
41
- "@types/chai": "^4.3.0",
39
+ "@iobroker/adapter-dev": "^1.0.0",
40
+ "@iobroker/adapter-react": "2.1.0",
41
+ "@iobroker/testing": "^2.6.0",
42
+ "@material-ui/core": "^4.12.4",
43
+ "@types/chai": "^4.3.1",
42
44
  "@types/chai-as-promised": "^7.1.5",
43
- "@types/gulp": "^4.0.9",
44
- "@types/mocha": "^9.1.0",
45
- "@types/node": "^17.0.23",
45
+ "@types/mocha": "^9.1.1",
46
+ "@types/node": "^17.0.35",
46
47
  "@types/proxyquire": "^1.3.28",
48
+ "@types/react-dom": "^18.0.3",
49
+ "@types/react": "^17.0.45",
47
50
  "@types/sinon": "^10.0.11",
48
51
  "@types/sinon-chai": "^3.2.8",
49
- "axios": "^0.26.1",
50
52
  "chai": "^4.3.6",
51
53
  "chai-as-promised": "^7.1.1",
52
- "eslint": "^8.12.0",
53
- "gulp": "^4.0.2",
54
- "mocha": "^9.2.2",
54
+ "eslint": "^8.16.0",
55
+ "eslint-plugin-react": "^7.30.0",
56
+ "mocha": "^10.0.0",
55
57
  "proxyquire": "^2.1.3",
56
- "sinon": "^13.0.1",
58
+ "react-dom": "^17.0.2",
59
+ "react": "^17.0.2",
60
+ "sinon": "^14.0.0",
57
61
  "sinon-chai": "^3.7.0",
58
- "typescript": "~4.6.3",
59
- "iobroker.js-controller": "4.0.21"
62
+ "typescript": "~4.7.2",
63
+ "iobroker.js-controller": "4.0.23"
60
64
  },
61
65
  "main": "main.js",
62
66
  "files": [
63
67
  "admin{,/!(src)/**}/!(tsconfig|tsconfig.*).json",
64
68
  "admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
69
+ "admin/build/",
65
70
  "lib/",
66
71
  "www/",
67
72
  "io-package.json",
@@ -69,13 +74,20 @@
69
74
  "main.js"
70
75
  ],
71
76
  "scripts": {
77
+ "prebuild": "rimraf admin/build",
78
+ "build": "build-adapter react",
79
+ "watch": "build-adapter react --watch",
80
+ "prebuild:react": "rimraf admin/build",
81
+ "build:react": "build-adapter react",
82
+ "watch:react": "build-adapter react --watch",
72
83
  "test:js": "mocha --exit --reporter-option maxDiffSize=50000 --config test/mocharc.custom.json \"{!(node_modules|test)/**/*.test.js,*.test.js,test/**/test!(PackageFiles|Startup).js}\"",
73
84
  "test:package": "mocha test/package --exit",
74
85
  "test:unit": "mocha test/unit --exit --timeout 3000",
75
86
  "test:integration": "mocha test/integration --exit --timeout 15000",
76
87
  "test": "npm run test:js && npm run test:package",
77
88
  "check": "tsc --noEmit -p tsconfig.check.json",
78
- "lint": "eslint",
89
+ "lint": "eslint --ext .js,.jsx",
90
+ "translate": "translate-adapter",
79
91
  "release": "release-script"
80
92
  },
81
93
  "bugs": {