iobroker.openknx 0.0.15 → 0.0.16

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
@@ -18,12 +18,12 @@ Please do not test in critical environments
18
18
 
19
19
  install in shell
20
20
  cd /opt/iobroker/node_modules
21
- npm i iobroker.openknx@latestversion
21
+ npm i iobroker.openknx
22
22
  iobroker add openknx
23
23
  npm i knx
24
24
 
25
25
  updates:
26
- npm i iobroker.openknx@latestversion
26
+ npm i iobroker.openknx
27
27
  iobroker upload openknx
28
28
 
29
29
  # GA import
@@ -68,7 +68,7 @@ Leave setting empty to use the adapters own namespace.
68
68
  },
69
69
  "force_encoding": "",
70
70
  "signedness": "",
71
- "valuetype": "basic"
71
+ "valuetype": "basic" //composite means set via a specific object
72
72
  },
73
73
  "from": "system.adapter.openknx.0",
74
74
  "user": "system.user.admin",
@@ -79,12 +79,40 @@ Leave setting empty to use the adapters own namespace.
79
79
  # Adapter communication Interface Description
80
80
  handeled DPTs 1-21,232,237,238
81
81
 
82
- Unhandeled DPTs are written as raw buffers, the iterface is a hexadecimal number.
82
+ Unhandeled DPTs are written as raw buffers, the iterface is a sequencial string of hexadecimal number. For example write '0102feff' to send values 0x01 0x02 0xfe 0xff on the bus.
83
83
  Where number, see scaling.
84
84
 
85
85
  Description of handeled DPTs
86
86
 
87
- tbd
87
+ javascript datatype special values range
88
+ DPT-1 boolean false, true
89
+ DPT-2 object {"priority":1 bit,"data":1 bit} -
90
+ DPT-3 object {"decr_incr":1 bit,"data":2 bit} -
91
+ DPT-18 object {"save_recall":0,"scenenumber":0}
92
+ DPT-21 object {"outofservice":0,"fault":0,"overridden":0,"inalarm":0,"alarmunack":0} -
93
+ DPT-232 object {red:0..255, green:0.255, blue:0.255} -
94
+ DPT-237 object {"address":0,"addresstype":0,"readresponse":0,"lampfailure":0,"ballastfailure":0,"convertorerror":0} -
95
+ DPT-4 string one character sent as 8-bit character
96
+ DPT-16 string one character sent as 16-character string
97
+ DPT-5 number 8-bit unsigned value
98
+ DPT-5.001 number 0..100 [%] scaled to 1-byte
99
+ DPT-5.003 number 0..360 [°] scaled to 1-byte
100
+ DPT-6 number 8-bit signed -128..127
101
+ DPT-7 number 16-bit unsigned value
102
+ DPT-8 number 2-byte signed value -32768..32767
103
+ DPT-9 number 2-byte floating point value
104
+ DPT-14 number 4-byte floating point value
105
+ DPT-12 number 4-byte unsigned value
106
+ DPT-13 number 4-byte signed value
107
+ DPT-15 number 4-byte
108
+ DPT-17 number 1-byte
109
+ DPT-20 number 1-byte
110
+ DPT-238 number 1-byte
111
+ DPT-10 number for Date Object -
112
+ DPT-11 number for Date Object -
113
+ DPT-19 number for Date Object -
114
+ rest object {0 .. } -
115
+
88
116
 
89
117
  Only time and date information is exchanged with KNX time based datatypes, e.g. DPT-19 has unsupported fields for signal quality
90
118
 
@@ -130,6 +158,7 @@ Select XML Output Format
130
158
  After the successful import a message shows how much objects where recognized.
131
159
  Press "save & close" or "save" to restart the adapter and take over the changes.
132
160
  When starting, the adapter tries to read all GroupAdresses with have the autoread flag (default setting). This could take a while and can produce a higher load on your KNX-bus. This ensures that the adapter operates with up-to-date values from the start.
161
+ Autoread is done on the first connection with the knx bus after an adapter start or restart, not on every knx reconnection.
133
162
 
134
163
  ## Objects
135
164
  In Objects the group adress tree like in your ETS project.
@@ -155,8 +184,10 @@ Wide DPT (datapoint type) support (DPT1 - DPT21, DPT232, DPT237, DPT238 supporte
155
184
  - only three level group address are supported
156
185
 
157
186
  ## Changelog
187
+ ### 0.0.16
188
+ * raw value handling
189
+
158
190
  ### 0.0.14
159
- * initial raw value handling added
160
191
  * import ga xml
161
192
 
162
193
  ### 0.0.12
package/admin/openknx.png CHANGED
Binary file
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "openknx",
4
- "version": "0.0.15",
4
+ "version": "0.0.16",
5
5
  "news": {
6
6
  "0.0.1": {
7
7
  "en": "initial release",
@@ -130,17 +130,17 @@ module.exports = {
130
130
  }
131
131
 
132
132
  // is there a scalar range? eg. DPT5.003 angle degrees (0=0, ff=360)
133
- if (dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('scalar_range')) {
133
+ if (!!(dptObj) && dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('scalar_range')) {
134
134
  range = dptObj.subtype.scalar_range;
135
- } else if (dptObj.hasOwnProperty('scalar_range')) {
135
+ } else if (!!(dptObj) && dptObj.hasOwnProperty('scalar_range')) {
136
136
  range = dptObj.scalar_range;
137
- } else if (dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('range')) {
137
+ } else if (!!(dptObj) && dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('range')) {
138
138
  // just a plain numeric value, only check if within bounds
139
139
  range = dptObj.subtype.range;
140
- } else if (!tools.isEmptyObject(dptObj) && dptObj.basetype.hasOwnProperty('range')) {
140
+ } else if (!!(dptObj) && dptObj.basetype.hasOwnProperty('range')) {
141
141
  // just a plain numeric value, only check if within bounds
142
142
  range = dptObj.basetype.range;
143
- } else if (!tools.isEmptyObject(dptObj)) {
143
+ } else if (!!(dptObj) && !tools.isEmptyObject(dptObj)) {
144
144
  //extracted from basetype
145
145
  range = [0, Math.pow(2, dptObj.basetype.bitlength) - 1];
146
146
  } else {
@@ -148,21 +148,28 @@ module.exports = {
148
148
  }
149
149
 
150
150
  if (tools.isDateDPT(dpt)) {
151
+ // we convert knx date types in javascript Date with different size
152
+ range[0] = undefined;
153
+ range[1] = undefined;
151
154
  type = 'number';
152
- } else if (!tools.isEmptyObject(dptObj) && dptObj.basetype.valuetype == 'composite') {
155
+ } else if (!!(dptObj) && !tools.isEmptyObject(dptObj) && dptObj.basetype.valuetype == 'composite') {
153
156
  range[0] = undefined;
154
157
  range[1] = undefined;
155
158
  type = 'object';
156
- } else if (!tools.isEmptyObject(dptObj) && dptObj.basetype.bitlength == 1) {
159
+ } else if (!!(dptObj) && !tools.isEmptyObject(dptObj) && dptObj.basetype.bitlength == 1) {
157
160
  type = 'boolean';
158
161
  } else if (tools.isStringDPT(dpt)) {
159
162
  range[0] = undefined;
160
163
  range[1] = undefined;
161
164
  type = 'string';
162
- } else if (tools.isUnknownDPT(dpt)) {
165
+ } else if (tools.isFloatDPT(dpt)) {
163
166
  range[0] = undefined;
164
167
  range[1] = undefined;
165
168
  type = 'number';
169
+ } else if (tools.isUnknownDPT(dpt)) {
170
+ range[0] = undefined;
171
+ range[1] = undefined;
172
+ type = 'string'; //raw
166
173
  } else {
167
174
  type = 'number';
168
175
  }
@@ -171,26 +178,32 @@ module.exports = {
171
178
  _id: fullPath,
172
179
  type: 'state',
173
180
  common: {
174
- desc: 'Basetype: ' + tools.isEmptyObject(dptObj) ? 'raw value' : dptObj.basetype.desc +
175
- ((dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('desc')) ? ', Subtype: ' + dptObj.subtype.desc : ''),
181
+ desc: 'Basetype: ' +
182
+ (!(dptObj) || tools.isEmptyObject(dptObj) ? 'raw value' :
183
+ (
184
+ dptObj.basetype.desc +
185
+ ((dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('desc')) ? (', Subtype: ' + dptObj.subtype.desc) : '')
186
+ )
187
+ ),
176
188
  min: range[0],
177
189
  max: range[1],
178
190
  name: gaName,
179
191
  read: true,
180
192
  role: '', // todo add default role
181
193
  type: type, //default is mixed==any type) (possible values: number, string, boolean, array, object, mixed, file). As exception the objects with type meta could have common.type=meta.user or meta.folder. It is important to note that array, object, mixed and file must be serialized using JSON.stringify().
182
- unit: (dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('unit')) ? dptObj.subtype.unit : '',
194
+ unit: (!!(dptObj) && dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('unit')) ? dptObj.subtype.unit : undefined,
183
195
  write: true,
184
196
  },
185
197
  native: {
186
198
  address: adr2ga(groupAddress.getAttribute('Address')),
199
+ answer_groupValueResponse: false, //overwrite manually
187
200
  autoread: true,
188
- bitlength: tools.isEmptyObject(dptObj) ? 'unknown' : dptObj.basetype.bitlength, //informative
201
+ bitlength: !(dptObj) || tools.isEmptyObject(dptObj) ? undefined : dptObj.basetype.bitlength, //informative
189
202
  dpt: dpt,
190
- encoding: dptObj.hasOwnProperty('subtype') ? (dptObj.subtype.hasOwnProperty('enc') ? dptObj.subtype.enc : dptObj.basetype.enc) : '', //informative ,todo matcht zu common.states`? hat basetype enc?
191
- force_encoding: (dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('force_encoding')) ? dptObj.subtype.force_encoding : '', //DPT16
192
- signedness: !tools.isEmptyObject(dptObj) ? (dptObj.basetype.hasOwnProperty('signedness') ? dptObj.basetype.signedness : '') : '', //signed or unsigned or empty
193
- valuetype: tools.isEmptyObject(dptObj) ? '' : dptObj.basetype.valuetype, //informative: composite or basic
203
+ encoding: !!(dptObj) && dptObj.hasOwnProperty('subtype') ? (dptObj.subtype.hasOwnProperty('enc') ? dptObj.subtype.enc : dptObj.basetype.enc) : undefined, //informative ,todo matcht zu common.states`? hat basetype enc?
204
+ force_encoding: (!!(dptObj) && dptObj.hasOwnProperty('subtype') && dptObj.subtype.hasOwnProperty('force_encoding')) ? dptObj.subtype.force_encoding : undefined, //DPT16
205
+ signedness: !!(dptObj) && !tools.isEmptyObject(dptObj) ? (dptObj.basetype.hasOwnProperty('signedness') ? dptObj.basetype.signedness : undefined) : undefined, //signed or unsigned or empty
206
+ valuetype: !(dptObj) || tools.isEmptyObject(dptObj) ? undefined : dptObj.basetype.valuetype, //informative: composite or basic
194
207
  }
195
208
  };
196
209
 
package/lib/tools.js CHANGED
@@ -119,6 +119,21 @@ function convertDPTtype(dpt) {
119
119
  return dpt;
120
120
  }
121
121
 
122
+ /* for testing, forward msg from one to another test address
123
+ better approach: send all test values via ets, send received value back from iobroker, compare in ets
124
+ */
125
+ function interfaceTest(id, state) {
126
+ const inpath = this.mynamespace + '.test.testin';
127
+ const outpath = this.mynamespace + '.test.testout';
128
+ if (id.startsWith(inpath)) {
129
+ var out = outpath + id.replace(inpath, '');
130
+ this.setForeignState(out, {
131
+ val: state.val,
132
+ ack: true,
133
+ });
134
+ }
135
+ }
136
+
122
137
  function isEmptyObject(obj) {
123
138
  return (obj
124
139
  && Object.keys(obj).length === 0
@@ -138,7 +153,15 @@ function isStringDPT(dpt) {
138
153
  }
139
154
 
140
155
  function isDateDPT(dpt) {
141
- return (dpt == 'DPT19' || dpt == 'DPT19' || dpt.startsWith('DPT19.') || dpt.startsWith('DPT19.'));
156
+ return (dpt == 'DPT19' || dpt.startsWith('DPT19.')
157
+ || dpt == 'DPT10' || dpt.startsWith('DPT10.')
158
+ );
159
+ }
160
+
161
+ function isFloatDPT(dpt) {
162
+ return (dpt == 'DPT9' || dpt.startsWith('DPT9.')
163
+ || dpt == 'DPT14' || dpt.startsWith('DPT14.')
164
+ );
142
165
  }
143
166
 
144
167
  function isUnknownDPT(dpt) {
@@ -147,7 +170,7 @@ function isUnknownDPT(dpt) {
147
170
  } catch (e) {
148
171
  return true;
149
172
  }
150
- return false;
173
+ return dptObj == null;
151
174
  }
152
175
 
153
176
  module.exports = {
@@ -159,5 +182,8 @@ module.exports = {
159
182
  isStringDPT,
160
183
  isDateDPT,
161
184
  isUnknownDPT,
185
+ isFloatDPT,
162
186
  isEmptyObject,
187
+ interfaceTest,
188
+
163
189
  };
package/main.js CHANGED
@@ -19,10 +19,8 @@ const tools = require('./lib/tools.js');
19
19
 
20
20
  class openknx extends utils.Adapter {
21
21
 
22
- mapping = {}; //todo join with states
23
- states = {};
24
- knxDatapoints = {}; //key array of knx.Datapoint known from mapping todo join
25
22
  gaList = new DoubleKeyedMap();
23
+ autoreaddone = false;
26
24
 
27
25
  knxConnection;
28
26
  mynamespace;
@@ -44,7 +42,6 @@ class openknx extends utils.Adapter {
44
42
  this.mynamespace = this.namespace;
45
43
  }
46
44
 
47
-
48
45
  /**
49
46
  * Is called when databases are connected and adapter received configuration.
50
47
  */
@@ -83,72 +80,6 @@ class openknx extends utils.Adapter {
83
80
  }
84
81
  }
85
82
 
86
- /**
87
- * Is called if a subscribed state changes
88
- * state.ack is coming in false if set by user (nodered, script...), here we set it.
89
- * https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/adapterdev.md
90
- * @param {string} id
91
- * @param {ioBroker.State | null | undefined} state
92
- */
93
- onStateChange(id, state) {
94
-
95
- var isRaw = false;
96
-
97
- if (!id) return;
98
- if (!state /*obj deleted*/ || typeof state !== 'object') return;
99
- //not a KNX object
100
- if (!this.states[id] || !this.states[id].native || this.states[id].native.address === undefined) return;
101
- if (!this.knxConnection) { //!knxConnection.connected != 'connected' todo check connection knxConncetion.initialState == initialized??
102
- this.log.warn('stateChange: not ready');
103
- return;
104
- }
105
-
106
- if (state.c == 'self') {
107
- //called by self, avoid loop
108
- //console.log('state change self id: ' + id);
109
- //this.interfaceTest(id, state);
110
- return;
111
- }
112
-
113
- var dpt = this.states[id].native.dpt;
114
- var ga = this.states[id].native.address;
115
- var val = state.val;
116
-
117
- //convert val into object for certain dpts
118
- if (tools.isDateDPT(dpt)) {
119
- val = new Date(val);
120
- } else if (!tools.isStringDPT(dpt)) {
121
- val = this.convertType(val);
122
- } else if (tools.isUnknownDPT(dpt)) {
123
- //write raw buffers for unknown dpts, iterface is a hex value
124
- //bitlength is the buffers bytelength * 8.
125
- val = Buffer.from(val, 'hex');
126
- isRaw = true;
127
- } else {
128
- this.log.warn('Missing implementation for unhandeled DPT, assuming number');
129
- }
130
-
131
- if (state.c == 'GroupValue_Read') {
132
- //interface to trigger GrouValue_Read is this comment
133
- this.log.debug('Outgoing GroupValue_Read to ' + ga + ' value ' + val);
134
- this.knxConnection.read(ga);
135
- if (!state.ack) this.setForeignState(id, {
136
- ack: true,
137
- c: 'self'
138
- });
139
- } else if (this.states[id].common.write) {
140
- this.log.debug('Outgoing GroupValue_Write to ' + ga + ' value ' + val + ' from ' + id);
141
- if (isRaw) ; //connection.writeRaw('1/0/0', val); //todo TEST
142
- else this.knxConnection.write(ga, val, dpt);
143
- if (!state.ack) this.setForeignState(id, {
144
- ack: true,
145
- c: 'self'
146
- });
147
- } else {
148
- this.log.warn('not configured write to ga: ' + val);
149
- }
150
- }
151
-
152
83
  // New message arrived. obj is array with current messages
153
84
  // triggered from admin page read in knx project
154
85
  onMessage(obj) {
@@ -217,7 +148,7 @@ class openknx extends utils.Adapter {
217
148
  * IOBroker Object tree cannot store 2 objects of same name, warn
218
149
  */
219
150
  warnDuplicates(objects) {
220
- //todo remove unallowed characted, global function
151
+ //todo remove unallowed character, lib function
221
152
  let arr = [];
222
153
  let duplicates = [];
223
154
  for (const object of objects) {
@@ -242,14 +173,13 @@ class openknx extends utils.Adapter {
242
173
  if (val instanceof Date) {
243
174
  //convert Date to number
244
175
  ret = Number(new Date(val));
176
+ } else if (Buffer.isBuffer(val)) {
177
+ //before object check
178
+ ret = val.toString('hex');
245
179
  } else if (typeof val === 'object') {
246
180
  ret = JSON.stringify(val);
247
181
  } else if (typeof val === 'string') {
248
- try {
249
- ret = JSON.parse(val); //string to object, the string types have to be filtered out before call
250
- } catch (e) {
251
- this.log.warn('write Object error, wrong format for dpt type ' + val);
252
- }
182
+ //use as is
253
183
  } else {
254
184
  //both can handle number and boolean
255
185
  ret = val;
@@ -257,47 +187,126 @@ class openknx extends utils.Adapter {
257
187
  return ret;
258
188
  }
259
189
 
260
- //interface to knx stack
261
- startKnxServer() {
190
+ /**
191
+ * Is called if a subscribed state changes
192
+ * state.ack is coming in false if set by user (nodered, script...), here we set it.
193
+ * https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/adapterdev.md
194
+ * @param {string} id
195
+ * @param {ioBroker.State | null | undefined} state
196
+ */
197
+ onStateChange(id, state) {
198
+ var isRaw = false;
199
+
200
+ if (!id) return;
201
+ if (!state /*obj deleted*/ || typeof state !== 'object') return;
202
+ //not a KNX object
203
+ if (!this.gaList.getDataById(id) || !this.gaList.getDataById(id).native || !this.gaList.getDataById(id).native.address) return;
204
+ if (!this.knxConnection) { //!knxConnection.connected != 'connected' todo check connection knxConncetion.initialState == initialized??
205
+ this.log.warn('stateChange: not ready');
206
+ return;
207
+ }
208
+
209
+ if (state.c == 'self') {
210
+ //called by self, avoid loop
211
+ //console.log('state change self id: ' + id);
212
+ //tools.interfaceTest(id, state);
213
+ return;
214
+ }
215
+
216
+ var dpt = this.gaList.getDataById(id).native.dpt;
217
+ var ga = this.gaList.getDataById(id).native.address;
218
+ var val = state.val;
262
219
 
263
- var cnt_complete = 0;
264
- var cnt_withDPT = 0;
220
+ //convert val into object for certain dpts
221
+ if (tools.isDateDPT(dpt)) {
222
+ //before composite check, date is also composite
223
+ val = new Date(val);
224
+ } else if (this.gaList.getDataById(id).native.valuetype == 'composite') {
225
+ try {
226
+ val = JSON.parse(val);
227
+ } catch (e) {
228
+ this.log.warn('stateChange: unsupported value format ' + val + ' for ' + ga);
229
+ return;
230
+ }
231
+ } else if (tools.isStringDPT(dpt)) {
232
+ ; //val = this.convertType(val);
233
+ } else if (tools.isUnknownDPT(dpt)) {
234
+ //write raw buffers for unknown dpts, iterface is a hex value
235
+ //bitlength is the buffers bytelength * 8.
236
+ val = Buffer.from(val, 'hex');
237
+ isRaw = true;
238
+ this.log.warn('Missing implementation for unhandeled DPT ' + dpt + ', assuming raw values');
239
+ } else {}
240
+
241
+ if (state.c == 'GroupValue_Read') {
242
+ //interface to trigger GrouValue_Read is this comment
243
+ this.log.debug('Outgoing GroupValue_Read to ' + ga + ' value ' + val);
244
+ this.knxConnection.read(ga);
245
+ if (!state.ack) this.setForeignState(id, {
246
+ ack: true,
247
+ c: 'self'
248
+ });
249
+ } else if (this.gaList.getDataById(id).common.write) {
250
+ this.log.debug('Outgoing GroupValue_Write to ' + ga + ' value ' + val + ' from ' + id);
251
+ if (isRaw) this.knxConnection.writeRaw(ga, val);
252
+ else this.knxConnection.write(ga, val, dpt);
253
+ if (!state.ack) this.setForeignState(id, {
254
+ ack: true,
255
+ c: 'self'
256
+ });
257
+ } else {
258
+ this.log.warn('not configured write to ga: ' + val);
259
+ }
260
+ }
265
261
 
262
+ //interface to knx stack
263
+ startKnxServer() {
266
264
  this.knxConnection = knx.Connection({
267
265
  ipAddr: this.config.gwip,
268
266
  ipPort: this.config.gwipport,
269
267
  physAddr: this.config.eibadr,
270
268
  minimumDelay: this.config.frameInterval,
271
269
  //map set the log level for messsages printed on the console. This can be 'error', 'warn', 'info' (default), 'debug', or 'trace'.
272
- //loglevel: this.log.level == 'silly' ? 'trace' : this.log.level,
270
+ loglevel: this.log.level == 'silly' ? 'trace' : this.log.level,
273
271
  //debug:
274
272
  handlers: {
275
273
  connected: () => {
276
- if (tools.isEmptyObject(this.knxDatapoints)) {
277
-
274
+ //create new knx datapoint and bind to connection
275
+ //in connected in order to have autoread work
276
+ var cnt_complete = 0;
277
+ var cnt_withDPT = 0;
278
+ if (!this.autoreaddone) {
279
+ //do autoread on start of adapter and not every connection
278
280
  for (const key of this.gaList) {
279
- if (this.gaList.getById(key).native.address.match(/\d*\/\d*\/\d*/) && this.gaList.getById(key).native.dpt) {
281
+ if (this.gaList.getDataById(key).native.address.match(/\d*\/\d*\/\d*/) && this.gaList.getDataById(key).native.dpt) {
280
282
  try {
281
- // act AND state datapoint
282
- this.knxDatapoints[this.gaList.getById(key).native.address] = new knx.Datapoint({
283
- ga: this.gaList.getById(key).native.address,
284
- dpt: this.gaList.getById(key).native.dpt,
285
- autoread: this.gaList.getById(key).native.autoread // issue a GroupValue_Read request to try to get the initial state from the bus (if any)
283
+ var dp = new knx.Datapoint({
284
+ ga: this.gaList.getDataById(key).native.address,
285
+ dpt: this.gaList.getDataById(key).native.dpt,
286
+ autoread: this.gaList.getDataById(key).native.autoread // issue a GroupValue_Read request to try to get the initial state from the bus (if any)
286
287
  }, this.knxConnection);
288
+ this.gaList.setDpById(key, dp);
287
289
  cnt_withDPT++;
288
- this.log.debug('Datapoint autoread created and GroupValueWrite sent: ' + this.gaList.getById(key).native.address + ' ' + key);
290
+ this.log.debug('Datapoint ' + (this.gaList.getDataById(key).native.autoread ? 'autoread ' : '') +
291
+ 'created and GroupValueWrite sent: ' + this.gaList.getDataById(key).native.address + ' ' + key);
289
292
  } catch (e) {
290
293
  this.log.warn('could not create KNX Datapoint for ' + key + ' with error: ' + e);
291
294
  }
292
295
  } else {
293
- this.log.debug('no match for ' + key);
296
+ this.log.warn('no match for ' + key);
294
297
  }
295
298
  cnt_complete++;
296
299
  }
300
+ this.autoreaddone = true;
301
+ this.log.info('Registered with ' + cnt_withDPT + ' KNX datapoints of ' + cnt_complete + ' datapoints in adapter.');
297
302
  }
298
-
299
303
  this.setState('info.connection', true, true);
300
- this.log.info('Connected! Registered with ' + cnt_withDPT + ' KNX datapoints of ' + cnt_complete + ' datapoints in adapter.');
304
+ this.log.info('Connected!');
305
+ },
306
+
307
+ disconnected: () => {
308
+ this.setState('info.connection', false, true);
309
+ this.log.info('Connection lost');
301
310
  },
302
311
 
303
312
  event: (evt, src, dest, val) => {
@@ -308,43 +317,45 @@ class openknx extends utils.Adapter {
308
317
  }
309
318
 
310
319
  /* some checks */
311
- if (!this.mapping[dest]) {
320
+ if (!this.gaList.getDpByAddress(dest)) {
312
321
  this.log.warn('Ignoring ' + evt + ' received on unknown GA: ' + dest);
313
322
  return;
314
323
  }
315
324
 
316
- var val = tools.isStringDPT(this.knxDatapoints[dest].dptid) ? this.knxDatapoints[dest].current_value : this.convertType(this.knxDatapoints[dest].current_value);
325
+ var val = tools.isStringDPT(this.gaList.getDataByAddress(dest).native.dpt) ?
326
+ this.gaList.getDpByAddress(dest).current_value :
327
+ this.convertType(this.gaList.getDpByAddress(dest).current_value);
317
328
 
318
329
  switch (evt) {
319
330
  case 'GroupValue_Read':
320
331
  //fetch val from addressed object and write on bus if configured to answer
321
- this.getForeignState(this.mapping[dest]._id, (err, state) => {
322
- this.log.debug('Incoming GroupValue_Read from ' + src + ' to ' + '(' + dest + ') ' + this.mapping[dest].common.name);
323
- if (this.mapping[dest].native.answer_groupValueResponse) {
332
+ this.getForeignState(this.gaList.getIdByAddress(dest), (err, state) => {
333
+ this.log.debug('Incoming GroupValue_Read from ' + src + ' to ' + '(' + dest + ') ' + this.gaList.getDataByAddress(dest).common.name);
334
+ if (this.gaList.getDataByAddress(dest).native.answer_groupValueResponse) {
324
335
  //https://bitbucket.org/ekarak/knx.js/issues/83/send-groupvalue_response
325
336
  //workaround, send out a write instead response
326
- this.knxConnection.write(dest, state.val, this.mapping[dest].native.dpt);
337
+ this.knxConnection.write(dest, state.val, this.gaList.getDataByAddress(dest).native.dpt);
327
338
  this.log.debug('responding with value ' + state.val);
328
339
  }
329
340
  });
330
341
  break;
331
342
 
332
343
  case 'GroupValue_Response':
333
- this.setForeignState(mapping[dest]._id, {
344
+ this.setForeignState(this.gaList.getIdByAddress(dest), {
334
345
  val: val,
335
346
  ack: true,
336
347
  c: 'self'
337
348
  });
338
- this.log.debug('Incoming GroupValue_Response from ' + src + ' to ' + '(' + dest + ') ' + this.mapping[dest].common.name + ': ' + this.knxDatapoints[dest].current_value);
349
+ this.log.debug('Incoming GroupValue_Response from ' + src + ' to ' + '(' + dest + ') ' + this.gaList.getDataByAddress(dest).common.name + ': ' + val);
339
350
  break;
340
351
 
341
352
  case 'GroupValue_Write':
342
- this.setForeignState(this.mapping[dest]._id, {
353
+ this.setForeignState(this.gaList.getIdByAddress(dest), {
343
354
  val: val,
344
355
  ack: true,
345
356
  c: 'self'
346
357
  });
347
- this.log.debug('Incoming GroupValue_Write ga: ' + dest + ' val: ' + this.knxDatapoints[dest].current_value + ' dpt: ' + this.knxDatapoints[dest].dptid + ' to Object: ' + this.mapping[dest]._id);
358
+ this.log.debug('Incoming GroupValue_Write ga: ' + dest + ' val: ' + val + ' dpt: ' + this.gaList.getDataByAddress(dest).native.dpt + ' to Object: ' + this.gaList.getIdByAddress(dest));
348
359
  break;
349
360
 
350
361
  default:
@@ -355,24 +366,13 @@ class openknx extends utils.Adapter {
355
366
  });
356
367
  }
357
368
 
358
- /* for testing, forward msg from one to another test address*/
359
- interfaceTest(id, state) {
360
- const inpath = this.mynamespace + '.test.testin';
361
- const outpath = this.mynamespace + '.test.testout';
362
- if (id.startsWith(inpath)) {
363
- var out = outpath + id.replace(inpath, '');
364
- this.setForeignState(out, {
365
- val: state.val,
366
- ack: true,
367
- });
368
- }
369
- }
370
-
371
369
  //todo: after override object path settings change load states again
372
370
  main() {
373
371
  this.log.info('Connecting to knx gateway: ' + this.config.gwip + ":" + this.config.gwipport + ' with phy. Adr: ' + this.config.eibadr + ' minimum send delay: ' + this.config.frameInterval);
374
372
  this.log.info(utils.controllerDir);
375
373
  this.setState('info.connection', false, true);
374
+
375
+ //fill gaList object from iobroker objects
376
376
  this.getObjectView('system', 'state', {
377
377
  startkey: this.mynamespace + '.',
378
378
  endkey: this.mynamespace + '.\u9999',
@@ -381,42 +381,58 @@ class openknx extends utils.Adapter {
381
381
  if (err) {
382
382
  this.log.error('Cannot get objects: ' + err);
383
383
  } else {
384
- this.states = {};
385
384
  for (var i = res.rows.length - 1; i >= 0; i--) {
386
385
  var id = res.rows[i].id;
387
- this.states[id] = res.rows[i].value; //list by object name todo remove
388
- this.mapping[res.rows[i].value.native.address] = res.rows[i].value; //list by ga todo remove
389
386
  this.gaList.set(id, res.rows[i].value.native.address, res.rows[i].value);
390
387
  }
391
388
  this.startKnxServer();
392
389
  }
393
390
  });
391
+
394
392
  }
395
393
  }
396
394
 
397
-
398
395
  class DoubleKeyedMap {
399
396
  constructor() {
397
+ //id, ga
400
398
  this.keymap = new Map();
399
+ //id, iobroker object
401
400
  this.data = new Map();
401
+ //id, knx dp
402
+ this.dp = new Map();
402
403
  }
404
+ //update or add
403
405
  set(id, address, data) {
404
- this.keymap.set(id, address);
406
+ this.keymap.set(address, id);
405
407
  this.data.set(id, data);
406
408
  }
407
- getById(id) {
409
+ //only dp returns transformed value, hold a reference to it
410
+ setDpById(id, dp) {
411
+ this.dp.set(id, dp);
412
+ }
413
+ getDpById(id) {
414
+ return this.dp.get(id);
415
+ }
416
+ getDpByAddress(address) {
417
+ return this.dp.get(this.keymap.get(address));
418
+ }
419
+ getDataById(id) {
408
420
  return this.data.get(id);
409
421
  }
410
- getByAddress(address) {
422
+ getDataByAddress(address) {
411
423
  return this.data.get(this.keymap.get(address));
412
424
  }
425
+ getIdByAddress(address) {
426
+ return this.keymap.get(address);
427
+ }
413
428
 
429
+ //key value is id
414
430
  [Symbol.iterator] = function () {
415
431
  return {
416
432
  index: -1,
417
433
  data: this.data,
418
434
  next() {
419
- return (this.index++ < this.data.size) ? {
435
+ return (++this.index < this.data.size) ? {
420
436
  done: false,
421
437
  value: Array.from(this.data.keys())[this.index]
422
438
  } : {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.openknx",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "dependencies": {
5
5
  "@iobroker/adapter-core": "^2.5.1",
6
6
  "binary-parser": "^1.1.5",