iobroker.zigbee 3.2.5 → 3.3.1-alpha.0
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 +16 -0
- package/admin/admin.js +376 -267
- package/admin/index_m.html +21 -32
- package/admin/tab_m.html +14 -2
- package/io-package.json +31 -31
- package/lib/commands.js +120 -76
- package/lib/exclude.js +1 -1
- package/lib/exposes.js +187 -77
- package/lib/groups.js +28 -15
- package/lib/{devices.js → legacy/devices.js} +27 -3
- package/lib/{states.js → legacy/states.js} +3 -3
- package/lib/localConfig.js +42 -0
- package/lib/models.js +615 -0
- package/lib/networkmap.js +15 -5
- package/lib/statescontroller.js +312 -297
- package/lib/utils.js +3 -4
- package/lib/zbBaseExtension.js +4 -0
- package/lib/zbDeviceAvailability.js +16 -23
- package/lib/zbDeviceConfigure.js +21 -8
- package/lib/zigbeecontroller.js +134 -88
- package/main.js +38 -42
- package/package.json +14 -15
package/lib/statescontroller.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const safeJsonStringify = require('./json');
|
|
4
4
|
const { EventEmitter } = require('events');
|
|
5
|
-
const
|
|
6
|
-
const { getAdId, getZbId, zbIdorIeeetoAdId } = require('./utils');
|
|
5
|
+
const modelDefinitions = require('./models');
|
|
7
6
|
const fs = require('fs');
|
|
8
7
|
const localConfig = require('./localConfig');
|
|
9
8
|
const path = require('path');
|
|
@@ -27,7 +26,12 @@ class StatesController extends EventEmitter {
|
|
|
27
26
|
this.debugMessages = { nodevice:{ in:[], out: []} };
|
|
28
27
|
this.debugActive = true;
|
|
29
28
|
this.deviceQueryBlock = [];
|
|
29
|
+
this.stashedErrors = { knownUnknownModels : new Set() }
|
|
30
30
|
this.clearStashedErrors();
|
|
31
|
+
this.commonStates = {};
|
|
32
|
+
for (const s of modelDefinitions.commonStates) {
|
|
33
|
+
this.commonStates[s.prop || s.id] = s;
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
info(message, data) {
|
|
@@ -55,119 +59,129 @@ class StatesController extends EventEmitter {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
clearStashedErrors() {
|
|
58
|
-
this.stashedErrors = {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
};
|
|
62
|
+
this.stashedErrors.errors = {};
|
|
63
|
+
this.stashedErrors.unknownModels = {},
|
|
64
|
+
this.stashedErrors.knownUnknownModels.clear();
|
|
65
|
+
this.stashedErrors.hasData = false;
|
|
63
66
|
return this.stashedErrors;
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
resetKnownUnknownModels() {
|
|
70
|
+
this.stashedErrors.unknownModels = {};
|
|
71
|
+
this.stashedErrors.knownUnknownModels.clear();
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
debugMessagesById() {
|
|
67
75
|
return this.debugMessages;
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
async clearModelDefinitions() {
|
|
79
|
+
await modelDefinitions.clearModelDefinitions();
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
async AddModelFromHerdsman(device, model) {
|
|
71
83
|
const namespace = `${this.adapter.name}.admin`;
|
|
84
|
+
const isLegacy = Boolean(this.localConfig.getModelOption(model, 'legacy'));
|
|
85
|
+
if (model === undefined) {
|
|
86
|
+
const dev_name = this.verifyDeviceName(utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false), model, device.modelID);
|
|
87
|
+
this.warn(`icon ${dev_name} for undefined Device not available. Check your devices.`);
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
72
90
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
const existingModelDesc = modelDefinitions.findModel(model, device.ieeeAddr, isLegacy);
|
|
92
|
+
const modelDesc = existingModelDesc ? existingModelDesc : await modelDefinitions.addExposeToDevices(device, model, this);
|
|
93
|
+
if (isLegacy) {
|
|
94
|
+
this.warn(`Trying to applying legacy definition for ${model} - ${modelDesc ? 'success.' : 'failed - no matching legacy model found'}`);
|
|
95
|
+
return;
|
|
76
96
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
if (!modelDesc || modelDesc == {}) return;
|
|
98
|
+
|
|
99
|
+
const srcIcon = (modelDesc && modelDesc.icon ? modelDesc.icon : '');
|
|
100
|
+
const model_modif = model.replace(/\//g, '-');
|
|
101
|
+
const pathToAdminIcon = `img/${model_modif}.png`;
|
|
102
|
+
// source is a web address
|
|
103
|
+
if (srcIcon.startsWith('http')) {
|
|
104
|
+
try {
|
|
105
|
+
//if (modelDesc) modelDesc.icon = pathToAdminIcon;
|
|
106
|
+
this.downloadIconToAdmin(srcIcon, pathToAdminIcon)
|
|
107
|
+
} catch (err) {
|
|
108
|
+
this.warn(`ERROR : unable to download ${srcIcon}: ${err && err.message ? err.message : 'no reason given'}`);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// source is inline basee64F
|
|
113
|
+
const base64Match = srcIcon.match(/data:image\/(.+);base64,/);
|
|
114
|
+
if (base64Match) {
|
|
115
|
+
this.warn(`base 64 Icon matched, trying to save it to disk as ${pathToAdminIcon}`);
|
|
116
|
+
if (modelDesc) modelDesc.icon = pathToAdminIcon;
|
|
117
|
+
this.adapter.fileExists(namespace, pathToAdminIcon, async (err,result) => {
|
|
118
|
+
if (result) {
|
|
119
|
+
this.info(`no need to save icon to ${pathToAdminIcon}`);
|
|
96
120
|
return;
|
|
97
121
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (result) {
|
|
105
|
-
this.warn(`no need to save icon to ${pathToAdminIcon}`);
|
|
122
|
+
const msg = `Saving base64 Data to ${pathToAdminIcon}`
|
|
123
|
+
try {
|
|
124
|
+
const buffer = Buffer.from(srcIcon.replace(base64Match[0],''), 'base64');
|
|
125
|
+
this.adapter.writeFile(namespace, pathToAdminIcon, buffer, (err) => {
|
|
126
|
+
if (err) {
|
|
127
|
+
this.warn(`${msg} -- failed: ${err?.message || 'no reason given'}`);
|
|
106
128
|
return;
|
|
107
129
|
}
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const buffer = Buffer.from(srcIcon.replace(base64Match[0],''), 'base64');
|
|
111
|
-
this.adapter.writeFile(namespace, pathToAdminIcon, buffer, (err) => {
|
|
112
|
-
if (err) {
|
|
113
|
-
this.warn(`${msg} -- failed: ${err && err.message ? err.message : 'no reason given'}`);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
this.debug(`${msg} -- success`);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
this.warn(`${msg} -- failed: ${err && err.message ? err.message : 'no reason given'}`)
|
|
121
|
-
}
|
|
130
|
+
this.debug(`${msg} -- success`);
|
|
122
131
|
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
this.warn(`${msg} -- failed: ${err?.message || 'no reason given'}`)
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// path is absolute
|
|
140
|
+
if (modelDesc) modelDesc.icon = pathToAdminIcon;
|
|
141
|
+
this.adapter.fileExists(namespace, pathToAdminIcon, async(err, result) => {
|
|
142
|
+
if (result) {
|
|
143
|
+
this.debug(`icon ${modelDesc?.icon || 'unknown icon'} found - no copy needed`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
// try 3 options for source file
|
|
147
|
+
let src = srcIcon; // as given
|
|
148
|
+
const locations=[];
|
|
149
|
+
if (!fs.existsSync(src)) {
|
|
150
|
+
src = path.normalize(this.adapter.expandFileName(src));
|
|
151
|
+
locations.push(src);
|
|
152
|
+
} // assumed relative to data folder
|
|
153
|
+
if (!fs.existsSync(src)) {
|
|
154
|
+
locations.push(src);
|
|
155
|
+
src = path.normalize(this.adapter.expandFileName(path.basename(src)));
|
|
156
|
+
} // filename relative to data folder
|
|
157
|
+
if (!fs.existsSync(src)) {
|
|
158
|
+
locations.push(src);
|
|
159
|
+
src = path.normalize(this.adapter.expandFileName(path.join('img',path.basename(src))));
|
|
160
|
+
} // img/<filename> relative to data folder
|
|
161
|
+
if (!fs.existsSync(src)) {
|
|
162
|
+
this.warn(`Unable to copy icon from device definition, looked at ${locations.join(', ')} and ${src}`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
fs.readFile(src, (err, data) => {
|
|
166
|
+
if (err) {
|
|
167
|
+
this.warn(`unable to read ${src}: ${(err.message? err.message:' no message given')}`);
|
|
123
168
|
return;
|
|
124
169
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.adapter.fileExists(namespace, pathToAdminIcon, async(err, result) => {
|
|
128
|
-
if (result) {
|
|
129
|
-
this.debug(`icon ${modelDesc ? modelDesc.icon : 'unknown icon'} found - no copy needed`);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
// try 3 options for source file
|
|
133
|
-
let src = srcIcon; // as given
|
|
134
|
-
const locations=[];
|
|
135
|
-
if (!fs.existsSync(src)) {
|
|
136
|
-
src = path.normalize(this.adapter.expandFileName(src));
|
|
137
|
-
locations.push(src);
|
|
138
|
-
} // assumed relative to data folder
|
|
139
|
-
if (!fs.existsSync(src)) {
|
|
140
|
-
locations.push(src);
|
|
141
|
-
src = path.normalize(this.adapter.expandFileName(path.basename(src)));
|
|
142
|
-
} // filename relative to data folder
|
|
143
|
-
if (!fs.existsSync(src)) {
|
|
144
|
-
locations.push(src);
|
|
145
|
-
src = path.normalize(this.adapter.expandFileName(path.join('img',path.basename(src))));
|
|
146
|
-
} // img/<filename> relative to data folder
|
|
147
|
-
if (!fs.existsSync(src)) {
|
|
148
|
-
this.warn(`Unable to copy icon from device definition, looked at ${locations.join(', ')} and ${src}`)
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
fs.readFile(src, (err, data) => {
|
|
170
|
+
if (data) {
|
|
171
|
+
this.adapter.writeFile(namespace, pathToAdminIcon, data, (err) => {
|
|
152
172
|
if (err) {
|
|
153
|
-
this.
|
|
173
|
+
this.error(`error writing file ${path}: ${err.message ? err.message : 'no reason given'}`);
|
|
154
174
|
return;
|
|
155
175
|
}
|
|
156
|
-
|
|
157
|
-
this.adapter.writeFile(namespace, pathToAdminIcon, data, (err) => {
|
|
158
|
-
if (err) {
|
|
159
|
-
this.error(`error writing file ${path}: ${err.message ? err.message : 'no reason given'}`);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
this.debug('Updated image file ' + pathToAdminIcon);
|
|
163
|
-
});
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
this.error(`fs.readFile failed - neither error nor data is returned!`);
|
|
176
|
+
this.debug('Updated image file ' + pathToAdminIcon);
|
|
167
177
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.error(`fs.readFile failed - neither error nor data is returned!`);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
return modelDesc;
|
|
184
|
+
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
async updateDebugDevices(debugDevices) {
|
|
@@ -255,8 +269,8 @@ class StatesController extends EventEmitter {
|
|
|
255
269
|
return;
|
|
256
270
|
}
|
|
257
271
|
|
|
258
|
-
const devId = getAdId(this.adapter, id); // iobroker device id
|
|
259
|
-
const deviceId = getZbId(id); // zigbee device id
|
|
272
|
+
const devId = utils.getAdId(this.adapter, id); // iobroker device id
|
|
273
|
+
const deviceId = utils.getZbId(id); // zigbee device id
|
|
260
274
|
|
|
261
275
|
if (this.checkDebugDevice(id)) {
|
|
262
276
|
const message = `User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`;
|
|
@@ -347,18 +361,20 @@ class StatesController extends EventEmitter {
|
|
|
347
361
|
|
|
348
362
|
async getDevStates(deviceId, model) {
|
|
349
363
|
try {
|
|
350
|
-
let states;
|
|
364
|
+
let states = [];
|
|
351
365
|
let stateModel;
|
|
366
|
+
// if i know the model is unknown, dont keep searching.
|
|
367
|
+
if (this.stashedErrors.knownUnknownModels.has(model)) return { states: modelDefinitions.commonStates };
|
|
352
368
|
if (model === 'group') {
|
|
353
|
-
states =
|
|
369
|
+
states = modelDefinitions.groupStates;
|
|
354
370
|
} else {
|
|
355
|
-
stateModel =
|
|
371
|
+
stateModel = modelDefinitions.findModel(model, utils.adIdtoZbIdorIeee(this.adapter,deviceId), this.localConfig.getModelOption(model, 'legacy', false));
|
|
356
372
|
if (!stateModel) {
|
|
357
373
|
// try to get the model from the exposes
|
|
358
374
|
this.stashUnknownModel(`getDevStates:${deviceId}`,`Model "${model}" not found for Device ${deviceId}`);
|
|
359
|
-
states =
|
|
375
|
+
states = modelDefinitions.commonStates;
|
|
360
376
|
} else {
|
|
361
|
-
states = stateModel.states;
|
|
377
|
+
states = modelDefinitions.commonStates.concat([...stateModel.states]);
|
|
362
378
|
}
|
|
363
379
|
if (typeof states === 'function' && !states.prototype) {
|
|
364
380
|
const entity = await this.adapter.zbController.resolveEntity(deviceId);
|
|
@@ -379,9 +395,15 @@ class StatesController extends EventEmitter {
|
|
|
379
395
|
if (!this.stashedErrors.errors.hasOwnProperty(key))
|
|
380
396
|
{
|
|
381
397
|
if (error) this.error(msg); else this.warn(msg);
|
|
382
|
-
this.stashedErrors.errors[key] = { message:msg, ts:[Date.now()], error:error };
|
|
398
|
+
this.stashedErrors.errors[key] = { message:msg, ts:[Date.now()], count: 1, error:error };
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
if (this.stashedErrors.errors[key].ts.length < 2)
|
|
402
|
+
this.stashedErrors.errors[key].ts.push(Date.now());
|
|
403
|
+
else
|
|
404
|
+
this.stashedErrors.errors[key].ts[1]=Date.now();
|
|
405
|
+
this.stashedErrors.errors[key].count++;
|
|
383
406
|
}
|
|
384
|
-
else this.stashedErrors.errors[key].ts.push(Date.now());
|
|
385
407
|
this.stashedErrors.hasData = true;
|
|
386
408
|
this.adapter.setState('info.lasterror', JSON.stringify({ error: key, data:this.stashedErrors.errors[key]}), true)
|
|
387
409
|
}
|
|
@@ -391,14 +413,20 @@ class StatesController extends EventEmitter {
|
|
|
391
413
|
stashUnknownModel(model, msg) {
|
|
392
414
|
try {
|
|
393
415
|
if (!this.stashedErrors.unknownModels.hasOwnProperty(model)) {
|
|
394
|
-
this.stashedErrors.unknownModels[model] = { message:msg, ts:[Date.now()], error:true };
|
|
416
|
+
this.stashedErrors.unknownModels[model] = { message:msg, ts:[Date.now()], count: 1, error:true };
|
|
395
417
|
this.error(`Unknown ${model}: ${msg}`)
|
|
396
418
|
}
|
|
397
|
-
else
|
|
419
|
+
else {
|
|
420
|
+
if (this.stashedErrors.unknownModels[model].count == 1) this.stashedErrors.unknownModels[model].ts.push(Date.now());
|
|
421
|
+
else
|
|
422
|
+
this.stashedErrors.unknownModels[model].ts[1] = Date.now();
|
|
423
|
+
this.stashedErrors.unknownModels[model].count++;
|
|
424
|
+
}
|
|
398
425
|
this.stashedErrors.hasData = true;
|
|
399
426
|
this.adapter.setState('info.lasterror', JSON.stringify({ model: model, data:this.stashedErrors.unknownModels[model]}), true)
|
|
427
|
+
return this.stashedErrors.unknownModels[model].count;
|
|
400
428
|
}
|
|
401
|
-
catch {
|
|
429
|
+
catch { return -1 }
|
|
402
430
|
}
|
|
403
431
|
|
|
404
432
|
async triggerComposite(_deviceId, stateDesc, interactive) {
|
|
@@ -468,11 +496,11 @@ class StatesController extends EventEmitter {
|
|
|
468
496
|
}
|
|
469
497
|
|
|
470
498
|
async publishFromStates(deviceId, model, stateIDs, options, debugID) {
|
|
471
|
-
const adId = zbIdorIeeetoAdId(this.adapter, deviceId, true);
|
|
499
|
+
const adId = utils.zbIdorIeeetoAdId(this.adapter, deviceId, true);
|
|
472
500
|
for (const stateID of Object.keys(stateIDs).sort((a,b) => {if (a=='device_query' || a=='state') return (b=='device_query' ? -1 : 1); else return a.localeCompare(b)})) {
|
|
473
501
|
const state = await this.adapter.getStateAsync(`${adId}.${stateID}`);
|
|
474
|
-
if (stateIDs[stateID]!= null) state.val = stateIDs[stateID];
|
|
475
502
|
if (state && state.hasOwnProperty('val')) {
|
|
503
|
+
if (stateIDs[stateID]!= null) state.val = stateIDs[stateID];
|
|
476
504
|
await this.publishFromState(deviceId, model, stateID, state, options, debugID)
|
|
477
505
|
}
|
|
478
506
|
}
|
|
@@ -496,8 +524,7 @@ class StatesController extends EventEmitter {
|
|
|
496
524
|
}
|
|
497
525
|
return;
|
|
498
526
|
}
|
|
499
|
-
const
|
|
500
|
-
const stateDesc = (commonStates === undefined ? devStates.states.find(statedesc => stateKey === statedesc.id) : commonStates);
|
|
527
|
+
const stateDesc = devStates.states.find(statedesc => stateKey === statedesc.id);
|
|
501
528
|
const stateModel = devStates.stateModel;
|
|
502
529
|
if (!stateDesc) {
|
|
503
530
|
const message = (`No state available for '${model}' with key '${stateKey}'`);
|
|
@@ -616,6 +643,12 @@ class StatesController extends EventEmitter {
|
|
|
616
643
|
this.adapter.extendObject(id, {common: {deactivated: inActive, color:inActive ? '#888888' : null, statusStates: inActive ? null : {onlineId:`${id}.available`} }});
|
|
617
644
|
}
|
|
618
645
|
|
|
646
|
+
async getDeviceActivated(id) {
|
|
647
|
+
|
|
648
|
+
const obj = await this.adapter.getObject(id);
|
|
649
|
+
return (obj?.common?.deactivated === false);
|
|
650
|
+
}
|
|
651
|
+
|
|
619
652
|
storeDeviceName(id, name) {
|
|
620
653
|
return this.localConfig.updateDeviceName(id, name);
|
|
621
654
|
}
|
|
@@ -625,9 +658,10 @@ class StatesController extends EventEmitter {
|
|
|
625
658
|
const options = { recursive:true };
|
|
626
659
|
try {
|
|
627
660
|
await this.adapter.delObjectAsync(devId,options);
|
|
661
|
+
return {status:true};
|
|
628
662
|
} catch (error) {
|
|
629
663
|
this.adapter.log.warn(`Cannot delete Object ${devId}: ${error && error.message ? error.message : 'without error message'}`);
|
|
630
|
-
return `Cannot delete Object ${devId}: ${error && error.message ? error.message : 'without error message'}
|
|
664
|
+
return { status:false, message: `Cannot delete Object ${devId}: ${error && error.message ? error.message : 'without error message'}`};
|
|
631
665
|
}
|
|
632
666
|
}
|
|
633
667
|
|
|
@@ -647,7 +681,7 @@ class StatesController extends EventEmitter {
|
|
|
647
681
|
|
|
648
682
|
async deleteOrphanedDeviceStates(ieeeAddr, model, force, callback, markOnly) {
|
|
649
683
|
const devStates = await this.getDevStates(ieeeAddr, model);
|
|
650
|
-
const commonStates =
|
|
684
|
+
const commonStates = modelDefinitions.commonStates || [];
|
|
651
685
|
const devId = utils.zbIdorIeeetoAdId(this.adapter, ieeeAddr, false);
|
|
652
686
|
const messages = [];
|
|
653
687
|
this.adapter.getStatesOf(devId, (err, states) => {
|
|
@@ -706,101 +740,93 @@ class StatesController extends EventEmitter {
|
|
|
706
740
|
setTimeout(() => this.updateState(dev_id, name, outValue, common), timeout);
|
|
707
741
|
}
|
|
708
742
|
|
|
709
|
-
async updateState(devId, name, value, common) {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
743
|
+
async updateState(devId, name, value, common, force) {
|
|
744
|
+
const new_common = {name: name, color:null};
|
|
745
|
+
const stateId = devId + '.' + name;
|
|
746
|
+
if (common) {
|
|
747
|
+
for (const key in common) {
|
|
748
|
+
if (common[key] !== undefined) new_common[key] = common[key];
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
// check if state exist
|
|
752
|
+
const stobj = await this.adapter.getObjectAsync(stateId);
|
|
753
|
+
let hasChanges = Boolean(force); // if force is set we always update the state.
|
|
754
|
+
if (stobj) {
|
|
755
|
+
// update state - not change name and role (user can it changed)
|
|
756
|
+
if (!force) {
|
|
757
|
+
if (stobj.common.name) {
|
|
758
|
+
delete new_common.name;
|
|
720
759
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
// check whether any common property is different
|
|
737
|
-
if (stobj.common) {
|
|
738
|
-
for (const property in new_common) {
|
|
739
|
-
if (stobj.common.hasOwnProperty(property)) {
|
|
740
|
-
if (stobj.common[property] === new_common[property]) {
|
|
741
|
-
delete new_common[property];
|
|
742
|
-
} else {
|
|
743
|
-
hasChanges = true;
|
|
744
|
-
}
|
|
760
|
+
}
|
|
761
|
+
// always force allow change of level.color roles.
|
|
762
|
+
if ((name.includes('color') || force) && (stobj.common.role != new_common.role)) {
|
|
763
|
+
console.info(`${force ? 'forc' : 'allow'}ing role change for ${name}`)
|
|
764
|
+
} else delete new_common.role;
|
|
765
|
+
|
|
766
|
+
// check whether any common property is different
|
|
767
|
+
if (!force) {
|
|
768
|
+
if (stobj.common) {
|
|
769
|
+
for (const property in new_common) {
|
|
770
|
+
if (stobj.common.hasOwnProperty(property)) {
|
|
771
|
+
if (stobj.common[property] === new_common[property]) {
|
|
772
|
+
delete new_common[property];
|
|
773
|
+
} else {
|
|
774
|
+
hasChanges = true;
|
|
745
775
|
}
|
|
746
776
|
}
|
|
747
777
|
}
|
|
748
|
-
} else {
|
|
749
|
-
const matches = stateId.match((/\./g));
|
|
750
|
-
if (matches && matches.length>1) {
|
|
751
|
-
const channels = stateId.split('.');
|
|
752
|
-
const SubChannels = [channels.shift()];
|
|
753
|
-
channels.pop();
|
|
754
|
-
for (const channel of channels) {
|
|
755
|
-
SubChannels.push(channel);
|
|
756
|
-
const id = SubChannels.join('.');
|
|
757
|
-
await this.adapter.extendObjectAsync(id, {type: 'channel', common: { name:channel}, native:{}})
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
hasChanges = true;
|
|
761
778
|
}
|
|
779
|
+
else hasChanges = true; // we dont have common => we need common
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
const matches = stateId.match((/\./g));
|
|
783
|
+
if (matches && matches.length>1) {
|
|
784
|
+
const channels = stateId.split('.');
|
|
785
|
+
const SubChannels = [channels.shift()];
|
|
786
|
+
channels.pop();
|
|
787
|
+
for (const channel of channels) {
|
|
788
|
+
SubChannels.push(channel);
|
|
789
|
+
const id = SubChannels.join('.');
|
|
790
|
+
await this.adapter.extendObjectAsync(id, {type: 'channel', common: { name:channel}, native:{}})
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
hasChanges = true;
|
|
794
|
+
}
|
|
762
795
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
nval = 0;
|
|
776
|
-
}
|
|
777
|
-
if (typeof minval == 'number' && nval < minval) {
|
|
778
|
-
hasChanges = true;
|
|
779
|
-
new_common.color = '#FF0000'
|
|
780
|
-
value = minval
|
|
781
|
-
this.stashErrors(`${stateId}.min`,`State value for ${stateId} has value "${nval}" less than min "${minval}".`, false );
|
|
782
|
-
}
|
|
783
|
-
if (typeof maxval == 'number' && nval > maxval) {
|
|
784
|
-
hasChanges = true;
|
|
785
|
-
hasChanges = true;
|
|
786
|
-
new_common.color = '#FF0000'
|
|
787
|
-
value = maxval;
|
|
788
|
-
this.stashErrors(`${stateId}.max`,`State value for ${stateId} has value "${nval}" greater than max "${maxval}".`, false );
|
|
789
|
-
}
|
|
790
|
-
}
|
|
796
|
+
// first check value
|
|
797
|
+
if (value !== undefined) {
|
|
798
|
+
const type = stobj ? stobj.common.type : new_common.type;
|
|
799
|
+
if (type === 'number') {
|
|
800
|
+
const minval = (stobj ? stobj.common.min : new_common.min);
|
|
801
|
+
const maxval = (stobj ? stobj.common.max : new_common.max);
|
|
802
|
+
let nval = (typeof value == 'number' ? value : parseFloat(value));
|
|
803
|
+
if (isNaN(nval)) {
|
|
804
|
+
if (minval !== undefined && typeof minval === 'number')
|
|
805
|
+
nval = minval;
|
|
806
|
+
else
|
|
807
|
+
nval = 0;
|
|
791
808
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
809
|
+
if (typeof minval == 'number' && nval < minval) {
|
|
810
|
+
hasChanges = true;
|
|
811
|
+
new_common.color = '#FF0000'
|
|
812
|
+
value = minval
|
|
813
|
+
this.stashErrors(`${stateId}.min`,`State value for ${stateId} has value "${nval}" less than min "${minval}".`, false );
|
|
814
|
+
}
|
|
815
|
+
if (typeof maxval == 'number' && nval > maxval) {
|
|
816
|
+
hasChanges = true;
|
|
817
|
+
new_common.color = '#FF0000'
|
|
818
|
+
value = maxval;
|
|
819
|
+
this.stashErrors(`${stateId}.max`,`State value for ${stateId} has value "${nval}" greater than max "${maxval}".`, false );
|
|
798
820
|
}
|
|
799
|
-
} else {
|
|
800
|
-
if (this.debugActive) this.debug(`UpdateState: Device is deactivated ${devId} ${JSON.stringify(obj)}`);
|
|
801
821
|
}
|
|
802
|
-
}
|
|
803
|
-
|
|
822
|
+
}
|
|
823
|
+
//
|
|
824
|
+
// only change object when any common property has changed
|
|
825
|
+
if (hasChanges) {
|
|
826
|
+
this.adapter.extendObject(stateId, {type: 'state', common: new_common, native: {}}, () =>
|
|
827
|
+
value !== undefined && this.setState_typed(stateId, value, true, stobj ? stobj.common.type : new_common.type));
|
|
828
|
+
} else if (value !== undefined) {
|
|
829
|
+
this.setState_typed(stateId, value, true, stobj.common.type);
|
|
804
830
|
}
|
|
805
831
|
}
|
|
806
832
|
|
|
@@ -851,21 +877,10 @@ class StatesController extends EventEmitter {
|
|
|
851
877
|
this.adapter.setState(id, value, ack, callback);
|
|
852
878
|
}
|
|
853
879
|
|
|
854
|
-
async applyLegacyDevices() {
|
|
855
|
-
const legacyModels = await this.localConfig.getLegacyModels();
|
|
856
|
-
const modelarr1 = [];
|
|
857
|
-
statesMapping.devices.forEach(item => modelarr1.push(item.models));
|
|
858
|
-
statesMapping.setLegacyDevices(legacyModels);
|
|
859
|
-
const modelarr2 = [];
|
|
860
|
-
statesMapping.devices.forEach(item => modelarr2.push(item.models));
|
|
861
|
-
}
|
|
862
|
-
|
|
863
880
|
async addLegacyDevice(model) {
|
|
864
|
-
|
|
865
|
-
statesMapping.getByModel();
|
|
881
|
+
return await modelDefinitions.findModel(model, undefined, undefined, true);
|
|
866
882
|
}
|
|
867
883
|
|
|
868
|
-
|
|
869
884
|
async getDefaultGroupIcon(id, members) {
|
|
870
885
|
let groupID = Number(id);
|
|
871
886
|
if (typeof id == 'string' && isNaN(groupID)) {
|
|
@@ -891,7 +906,10 @@ class StatesController extends EventEmitter {
|
|
|
891
906
|
const __dev_name = this.verifyDeviceName(dev_id, model, (dev_name ? dev_name : model));
|
|
892
907
|
if (this.debugActive) this.debug(`UpdateDev called with ${dev_id}, ${dev_name}, ${model}, ${__dev_name}`);
|
|
893
908
|
const id = '' + dev_id;
|
|
894
|
-
const
|
|
909
|
+
const objId = model=='group' ? `group_${dev_id}` : dev_id;
|
|
910
|
+
const obj = await this.adapter.getObjectAsync( objId);
|
|
911
|
+
this.adapter.zbController.setDeviceEnable(dev_id, (obj?.common?.disabled == true));
|
|
912
|
+
const modelDesc = await modelDefinitions.findModel(model, `0x${dev_id}`, this.localConfig.getModelOption(model, 'legacy'));
|
|
895
913
|
const modelIcon = (model == 'group' ?
|
|
896
914
|
await this.getDefaultGroupIcon(dev_id) :
|
|
897
915
|
modelDesc && modelDesc.icon ? modelDesc.icon : 'img/unknown.png');
|
|
@@ -914,9 +932,6 @@ class StatesController extends EventEmitter {
|
|
|
914
932
|
}
|
|
915
933
|
}
|
|
916
934
|
}
|
|
917
|
-
const objId = model=='group' ? `group_${dev_id}` : dev_id;
|
|
918
|
-
|
|
919
|
-
const obj = await this.adapter.getObjectAsync( objId);
|
|
920
935
|
|
|
921
936
|
const myCommon = {
|
|
922
937
|
name: __dev_name,
|
|
@@ -1073,7 +1088,7 @@ class StatesController extends EventEmitter {
|
|
|
1073
1088
|
}
|
|
1074
1089
|
}
|
|
1075
1090
|
|
|
1076
|
-
async syncDevStates(dev, model) {
|
|
1091
|
+
async syncDevStates(dev, model, force) {
|
|
1077
1092
|
if (this.debugActive) this.debug('synchronizing device states for ' + dev.ieeeAddr + ' (' + model + ')');
|
|
1078
1093
|
const devId = utils.zbIdorIeeetoAdId(this.adapter, dev.ieeeAddr, false);
|
|
1079
1094
|
// devId - iobroker device id
|
|
@@ -1081,7 +1096,7 @@ class StatesController extends EventEmitter {
|
|
|
1081
1096
|
if (!devStates) {
|
|
1082
1097
|
return;
|
|
1083
1098
|
}
|
|
1084
|
-
const states =
|
|
1099
|
+
const states = devStates.states;
|
|
1085
1100
|
|
|
1086
1101
|
for (const stateInd in states) {
|
|
1087
1102
|
if (!states.hasOwnProperty(stateInd)) {
|
|
@@ -1094,10 +1109,11 @@ class StatesController extends EventEmitter {
|
|
|
1094
1109
|
return;
|
|
1095
1110
|
}
|
|
1096
1111
|
// Filter out non routers or devices that are battery driven for the availability flag
|
|
1097
|
-
if (statedesc.id === 'available') {
|
|
1098
|
-
if (!(dev.type === 'Router') || dev.powerSource === 'Battery') {
|
|
1112
|
+
if (statedesc.id === 'available' && dev.type != 'Router' && dev.type != 'EndDevice') {
|
|
1113
|
+
/* if (!(dev.type === 'Router') || dev.powerSource === 'Battery') {
|
|
1099
1114
|
continue;
|
|
1100
|
-
}
|
|
1115
|
+
} */
|
|
1116
|
+
continue;
|
|
1101
1117
|
}
|
|
1102
1118
|
// lazy states
|
|
1103
1119
|
if (statedesc.lazy) {
|
|
@@ -1116,22 +1132,11 @@ class StatesController extends EventEmitter {
|
|
|
1116
1132
|
max: statedesc.max,
|
|
1117
1133
|
states: statedesc.states,
|
|
1118
1134
|
};
|
|
1119
|
-
this.updateState(devId, statedesc.id, undefined, common);
|
|
1135
|
+
this.updateState(devId, statedesc.id, undefined, common, force);
|
|
1120
1136
|
}
|
|
1121
1137
|
this.deleteOrphanedDeviceStates(dev.ieeeAddr, model, false, undefined, true);
|
|
1122
1138
|
}
|
|
1123
1139
|
|
|
1124
|
-
async getExposes() {
|
|
1125
|
-
await this.localConfig.init();
|
|
1126
|
-
await this.applyLegacyDevices();
|
|
1127
|
-
try {
|
|
1128
|
-
statesMapping.fillStatesWithExposes(this);
|
|
1129
|
-
}
|
|
1130
|
-
catch (error) {
|
|
1131
|
-
this.error(`Error applying exposes: ${error && error.message ? error.message : 'no error message'} ${error && error.stack ? error.stack : ''}`);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
1140
|
async elevatedMessage(device, message, isError) {
|
|
1136
1141
|
if (isError) this.error(message); else this.warn(message);
|
|
1137
1142
|
// emit data here for debug tab later
|
|
@@ -1143,6 +1148,76 @@ class StatesController extends EventEmitter {
|
|
|
1143
1148
|
this.emit('debugmessage', {id: id, message:message});
|
|
1144
1149
|
}
|
|
1145
1150
|
|
|
1151
|
+
async publishToDefinedState(devId, model, statedesc, payload, debugId) {
|
|
1152
|
+
const has_elevated_debug = (this.checkDebugDevice(devId) && !payload.hasOwnProperty('msg_from_zigbee'));
|
|
1153
|
+
let value = undefined;
|
|
1154
|
+
let mode = 'prop/id'
|
|
1155
|
+
if (statedesc.getter) {
|
|
1156
|
+
value = statedesc.getter(payload);
|
|
1157
|
+
mode = 'getter'
|
|
1158
|
+
} else {
|
|
1159
|
+
value = payload[statedesc.prop];
|
|
1160
|
+
if (value === undefined || value === null) value = payload[statedesc.id];
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
let stateID = statedesc.id;
|
|
1164
|
+
|
|
1165
|
+
// checking value
|
|
1166
|
+
if (value === undefined || value === null) {
|
|
1167
|
+
const message = `value '${JSON.stringify(value)}' from device ${devId} via ${mode} for '${statedesc.name} (ID ${statedesc.id} ${statedesc.prop ? 'with property '+statedesc.prop : ''})`;
|
|
1168
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { states:[{id:stateID, value:value, payload:payload }],flag:'04', IO:true }, message});
|
|
1169
|
+
else if (this.debugActive) this.debug(message);
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
const message = `value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`;
|
|
1175
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { states:[{id:stateID, value:value, payload:payload }],flag:'04', IO:true }, message});
|
|
1176
|
+
else if (this.debugActive) this.debug(message);
|
|
1177
|
+
|
|
1178
|
+
const common = {
|
|
1179
|
+
name: statedesc.name,
|
|
1180
|
+
type: statedesc.type,
|
|
1181
|
+
unit: statedesc.unit,
|
|
1182
|
+
read: statedesc.read,
|
|
1183
|
+
write: statedesc.write,
|
|
1184
|
+
icon: statedesc.icon,
|
|
1185
|
+
role: statedesc.role,
|
|
1186
|
+
min: statedesc.min,
|
|
1187
|
+
max: statedesc.max,
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
|
|
1191
|
+
stateID = `${stateID}.${value.stateid}`;
|
|
1192
|
+
if (value.hasOwnProperty('unit')) {
|
|
1193
|
+
common.unit = value.unit;
|
|
1194
|
+
}
|
|
1195
|
+
common.name = value.name ? value.name : value.stateid;
|
|
1196
|
+
common.role = value.role ? `value.${value.role}` : 'number';
|
|
1197
|
+
value = value.value;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// if needs to return value to back after timeout
|
|
1201
|
+
if (statedesc.isEvent) {
|
|
1202
|
+
this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, (typeof value == typeof (!value) ? !value : ''));
|
|
1203
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
1204
|
+
|
|
1205
|
+
} else {
|
|
1206
|
+
if (statedesc.prepublish) {
|
|
1207
|
+
this.collectOptions(devId, model, false, options =>
|
|
1208
|
+
statedesc.prepublish(devId, value, newvalue => {
|
|
1209
|
+
this.updateState(devId, stateID, newvalue, common) }, options)
|
|
1210
|
+
);
|
|
1211
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
1212
|
+
} else {
|
|
1213
|
+
this.updateState(devId, stateID, value, common, debugId);
|
|
1214
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return true;
|
|
1218
|
+
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1146
1221
|
async publishToState(devId, model, payload, debugId) {
|
|
1147
1222
|
try {
|
|
1148
1223
|
if (!debugId) debugId = Date.now();
|
|
@@ -1165,70 +1240,12 @@ class StatesController extends EventEmitter {
|
|
|
1165
1240
|
let has_published = false;
|
|
1166
1241
|
if (devStates.states !== undefined) {
|
|
1167
1242
|
try {
|
|
1168
|
-
const states =
|
|
1169
|
-
devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id))
|
|
1243
|
+
const states = devStates.states.filter(statedesc => payload.hasOwnProperty(statedesc.prop || statedesc.id)
|
|
1170
1244
|
);
|
|
1171
1245
|
|
|
1172
1246
|
for (const stateInd in states) {
|
|
1173
1247
|
const statedesc = states[stateInd];
|
|
1174
|
-
|
|
1175
|
-
if (statedesc.getter) {
|
|
1176
|
-
value = statedesc.getter(payload);
|
|
1177
|
-
} else {
|
|
1178
|
-
value = payload[statedesc.prop || statedesc.id];
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
// checking value
|
|
1182
|
-
if (value === undefined || value === null) {
|
|
1183
|
-
continue;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
let stateID = statedesc.id;
|
|
1187
|
-
const message = `value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`;
|
|
1188
|
-
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { states:[{id:stateID, value:value, payload:payload }],flag:'04', IO:true }, message});
|
|
1189
|
-
else if (this.debugActive) this.debug(message);
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
const common = {
|
|
1193
|
-
name: statedesc.name,
|
|
1194
|
-
type: statedesc.type,
|
|
1195
|
-
unit: statedesc.unit,
|
|
1196
|
-
read: statedesc.read,
|
|
1197
|
-
write: statedesc.write,
|
|
1198
|
-
icon: statedesc.icon,
|
|
1199
|
-
role: statedesc.role,
|
|
1200
|
-
min: statedesc.min,
|
|
1201
|
-
max: statedesc.max,
|
|
1202
|
-
};
|
|
1203
|
-
|
|
1204
|
-
if (typeof value === 'object' && value.hasOwnProperty('stateid')) {
|
|
1205
|
-
stateID = `${stateID}.${value.stateid}`;
|
|
1206
|
-
if (value.hasOwnProperty('unit')) {
|
|
1207
|
-
common.unit = value.unit;
|
|
1208
|
-
}
|
|
1209
|
-
common.name = value.name ? value.name : value.stateid;
|
|
1210
|
-
common.role = value.role ? `value.${value.role}` : 'number';
|
|
1211
|
-
value = value.value;
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
// if needs to return value to back after timeout
|
|
1215
|
-
if (statedesc.isEvent) {
|
|
1216
|
-
this.updateStateWithTimeout(devId, statedesc.id, value, common, 300, (typeof value == typeof (!value) ? !value : ''));
|
|
1217
|
-
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
1218
|
-
|
|
1219
|
-
} else {
|
|
1220
|
-
if (statedesc.prepublish) {
|
|
1221
|
-
this.collectOptions(devId, model, false, options =>
|
|
1222
|
-
statedesc.prepublish(devId, value, newvalue => {
|
|
1223
|
-
this.updateState(devId, stateID, newvalue, common) }, options)
|
|
1224
|
-
);
|
|
1225
|
-
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
1226
|
-
} else {
|
|
1227
|
-
this.updateState(devId, stateID, value, common, debugId);
|
|
1228
|
-
if (has_elevated_debug) this.emit('device_debug', { ID:debugId, data: { flag:'SUCCESS', IO:true }});
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
has_published = true;
|
|
1248
|
+
has_published |= await this.publishToDefinedState(devId, model, statedesc, payload, debugId);
|
|
1232
1249
|
}
|
|
1233
1250
|
} catch (e) {
|
|
1234
1251
|
const message = `unable to enumerate states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`;
|
|
@@ -1348,7 +1365,7 @@ class StatesController extends EventEmitter {
|
|
|
1348
1365
|
const has_elevated_debug = this.checkDebugDevice(devId);
|
|
1349
1366
|
const debugId = Date.now();
|
|
1350
1367
|
if (entity.device.interviewing) {
|
|
1351
|
-
this.
|
|
1368
|
+
this.debug(`zigbee event for ${device.ieeeAddr} received during interview!`);
|
|
1352
1369
|
return;
|
|
1353
1370
|
}
|
|
1354
1371
|
|
|
@@ -1407,19 +1424,17 @@ class StatesController extends EventEmitter {
|
|
|
1407
1424
|
|
|
1408
1425
|
// always publish link_quality and battery
|
|
1409
1426
|
if (message.linkquality) { // send battery with
|
|
1410
|
-
this.
|
|
1427
|
+
this.publishToDefinedState(devId, model, this.commonStates.linkquality, {linkquality: message.linkquality}, debugId);
|
|
1411
1428
|
if (isBattKey) {
|
|
1412
|
-
this.publishToState(devId, model, {voltage: _voltage}, debugId);
|
|
1413
1429
|
const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
|
|
1414
|
-
this.publishToState(devId, model, {battery: battProz}, debugId);
|
|
1430
|
+
this.publishToState(devId, model, {voltage: _voltage, battery: battProz}, debugId);
|
|
1415
1431
|
}
|
|
1416
1432
|
if (isMessure) {
|
|
1417
|
-
this.publishToState(devId, model, {temperature: _temperature}, debugId);
|
|
1418
|
-
this.publishToState(devId, model, {humidity: _humidity}), debugId;
|
|
1433
|
+
this.publishToState(devId, model, {temperature: _temperature, humidity: _humidity}, debugId);
|
|
1419
1434
|
}
|
|
1420
1435
|
}
|
|
1421
1436
|
|
|
1422
|
-
this.
|
|
1437
|
+
this.publishToDefinedState(devId, model, this.commonStates.msg_from_zigbee, {msg_from_zigbee: safeJsonStringify(msgForState)}, -1);
|
|
1423
1438
|
|
|
1424
1439
|
if (!entity.mapped) {
|
|
1425
1440
|
return;
|