iobroker.zigbee 3.1.4 → 3.1.5
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 +5 -0
- package/admin/admin.js +745 -632
- package/admin/index_m.html +26 -150
- package/admin/tab_m.html +137 -238
- package/io-package.json +15 -15
- package/lib/DeviceDebug.js +0 -1
- package/lib/commands.js +69 -35
- package/lib/statescontroller.js +1 -1
- package/lib/zbDeviceEvent.js +0 -1
- package/lib/zigbeecontroller.js +11 -7
- package/main.js +14 -21
- package/package.json +3 -3
package/admin/admin.js
CHANGED
|
@@ -64,23 +64,89 @@ const savedSettings = [
|
|
|
64
64
|
'adapterType', 'debugHerdsman', 'disableBackup', 'external', 'startWithInconsistent','pingTimeout','listDevicesAtStart',
|
|
65
65
|
'warnOnDeviceAnnouncement', 'baudRate', 'flowCTRL', 'autostart', 'readAtAnnounce', 'startReadDelay', 'readAllAtStart','pingCluster'
|
|
66
66
|
];
|
|
67
|
+
const lockout = {
|
|
68
|
+
timeoutid:undefined,
|
|
69
|
+
isActive:false,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const connectionStatus = {
|
|
73
|
+
connected: false,
|
|
74
|
+
lastcheck: Date.now(),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
////
|
|
79
|
+
//
|
|
80
|
+
//. section Alive
|
|
81
|
+
//
|
|
82
|
+
////
|
|
83
|
+
|
|
84
|
+
function keepAlive(callback) {
|
|
85
|
+
const responseTimeout = setTimeout(function() {
|
|
86
|
+
UpdateAdapterAlive(false); }, 500);
|
|
87
|
+
sendTo(namespace, 'aliveCheck', {}, function(msg) {
|
|
88
|
+
clearTimeout(responseTimeout);
|
|
89
|
+
UpdateAdapterAlive(true);
|
|
90
|
+
if (callback) callback();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function startKeepalive() {
|
|
95
|
+
return setInterval(keepAlive, 10000);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function UpdateAdapterAlive(state) {
|
|
99
|
+
if (connectionStatus.connected === state) return;
|
|
100
|
+
connectionStatus.time = Date.now();
|
|
101
|
+
if (state) {
|
|
102
|
+
$('#adapterStopped_btn').addClass('hide');
|
|
103
|
+
$('#code_pairing').removeClass('disabled');
|
|
104
|
+
$('#touchlink_btn').removeClass('disabled');
|
|
105
|
+
$('#add_grp_btn').removeClass('disabled');
|
|
106
|
+
$('#fw_check_btn').removeClass('disabled');
|
|
107
|
+
$('#ErrorNotificationBtn').removeClass('disabled');
|
|
108
|
+
$('#show_errors_btn').removeClass('disabled');
|
|
109
|
+
$('#download_icons_btn').removeClass('disabled');
|
|
110
|
+
$('#pairing').removeClass('disabled');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
$('#adapterStopped_btn').removeClass('hide');
|
|
114
|
+
$('#code_pairing').addClass('disabled');
|
|
115
|
+
$('#touchlink_btn').addClass('disabled');
|
|
116
|
+
$('#add_grp_btn').addClass('disabled');
|
|
117
|
+
$('#fw_check_btn').addClass('disabled');
|
|
118
|
+
$('#ErrorNotificationBtn').addClass('disabled');
|
|
119
|
+
$('#show_errors_btn').addClass('disabled');
|
|
120
|
+
$('#pairing').addClass('disabled');
|
|
121
|
+
$('#download_icons_btn').addClass('disabled');
|
|
122
|
+
}
|
|
123
|
+
connectionStatus.connected = state;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
////
|
|
128
|
+
//
|
|
129
|
+
// Utility functions
|
|
130
|
+
//
|
|
131
|
+
////
|
|
132
|
+
function sendToWrapper(target,command,msg,callback) {
|
|
133
|
+
if (connectionStatus.connected)
|
|
134
|
+
sendTo(target,command,msg,callback);
|
|
135
|
+
else if (callback) callback({error:'Cannot execute command - adapter is not running'});
|
|
136
|
+
}
|
|
67
137
|
|
|
68
138
|
function getDeviceByID(ID) {
|
|
69
139
|
if (devices) return devices.find((devInfo) => {
|
|
70
|
-
|
|
71
|
-
return devInfo._id == ID;
|
|
72
|
-
} catch (e) {
|
|
73
|
-
//console.log("No dev with ieee " + ieeeAddr);
|
|
74
|
-
}
|
|
140
|
+
return (devInfo ? devInfo._id : '') == ID;
|
|
75
141
|
});
|
|
76
142
|
}
|
|
77
143
|
|
|
78
|
-
function
|
|
144
|
+
function getDeviceByIEEE(ieeeAddr) {
|
|
79
145
|
return devices.find((devInfo) => {
|
|
80
146
|
try {
|
|
81
147
|
return devInfo.info.device.ieee == ieeeAddr;
|
|
82
148
|
} catch (e) {
|
|
83
|
-
|
|
149
|
+
return false;
|
|
84
150
|
}
|
|
85
151
|
});
|
|
86
152
|
}
|
|
@@ -91,7 +157,7 @@ function getDeviceByNetwork(nwk) {
|
|
|
91
157
|
try {
|
|
92
158
|
return devInfo.info.device.nwk == nwk;
|
|
93
159
|
} catch (e) {
|
|
94
|
-
|
|
160
|
+
return false;
|
|
95
161
|
}
|
|
96
162
|
});
|
|
97
163
|
}
|
|
@@ -112,6 +178,198 @@ function getLQICls(value) {
|
|
|
112
178
|
return 'icon-green';
|
|
113
179
|
}
|
|
114
180
|
|
|
181
|
+
function sanitizeModelParameter(parameter) {
|
|
182
|
+
const replaceByUnderscore = /[\s/]/g;
|
|
183
|
+
return parameter.replace(replaceByUnderscore, '_');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/////
|
|
187
|
+
//
|
|
188
|
+
// Section Local Data
|
|
189
|
+
//
|
|
190
|
+
////
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
function getModelData(data) {
|
|
194
|
+
console.warn(JSON.stringify(data));
|
|
195
|
+
const devicesByModel = {};
|
|
196
|
+
for (const dev of data) {
|
|
197
|
+
const modelID = dev.info?.mapped?.model || dev.info.device.modelZigbee || dev.info.device.name || 'unknown';
|
|
198
|
+
if (devicesByModel[modelID])
|
|
199
|
+
devicesByModel[modelID].devices.push(dev);
|
|
200
|
+
else devicesByModel[modelID] = {devices:[dev], icon:dev.common.icon};
|
|
201
|
+
}
|
|
202
|
+
console.warn(JSON.stringify(devicesByModel));
|
|
203
|
+
const Html = [];
|
|
204
|
+
// Html.push(`<ul class="collapsible">`);
|
|
205
|
+
Html.push(`<ul class="collection">`)
|
|
206
|
+
for (const key of Object.keys(devicesByModel)) {
|
|
207
|
+
const model = devicesByModel[key];
|
|
208
|
+
Html.push(`<li class="collection-item avatar>`);
|
|
209
|
+
//Html.push(`<li>`)
|
|
210
|
+
//Html.push(`<div class="collapsible-header"><img src=${model.iccon} alt="" class="circle" width="40" height="auto"> Paired Models</div>`);
|
|
211
|
+
//Html.push(`<div class="collapsile-body"<span>${getDeviceData(model.devices)}</span></div>`);
|
|
212
|
+
Html.push(`<img src = ${model.iccon} alt="" class="circle" width="40" height="auto">`);
|
|
213
|
+
Html.push(`<span class=title></p>`);
|
|
214
|
+
Html.push(getDeviceData(model.devices).join('<br>'))
|
|
215
|
+
Html.push(`</p><a href="#!" class="secondary-content"><i class="material-icons">grade</i></a></li>`)
|
|
216
|
+
}
|
|
217
|
+
Html.push('</ul>');
|
|
218
|
+
return Html;
|
|
219
|
+
}
|
|
220
|
+
function getDeviceData(deviceList, withIcon) {
|
|
221
|
+
const Html = [`<div class="container">`];
|
|
222
|
+
for (const dev of deviceList) {
|
|
223
|
+
const iconLink = `<img src=${dev.common.icon} class="circle" width="40" height="auto">`;
|
|
224
|
+
Html.push(`<div="row"><div class="col s4">${withIcon ? iconLink : ''}<br>${dev.info.device.ieee}<br>connectedInfo</div>`)
|
|
225
|
+
Html.push(`<div class=col s4>Device Name:${dev.common.name}</div><div class=col s4>Connected: true</div></div>`);
|
|
226
|
+
if (dev.options) {
|
|
227
|
+
Html.push(`<div="row"><div class="col s3">Options</div>`)
|
|
228
|
+
for (const o of dev.options) {
|
|
229
|
+
Html.push(`<div class=col s4>${o.key}</div><div class=col s4>${o.value}</div><div>`);
|
|
230
|
+
}
|
|
231
|
+
Html.push(`</div>`);
|
|
232
|
+
}
|
|
233
|
+
Html.push(`</div>`)
|
|
234
|
+
}
|
|
235
|
+
Html.push(`</div>`)
|
|
236
|
+
return Html;
|
|
237
|
+
}
|
|
238
|
+
function getGlobalOptionData() {
|
|
239
|
+
return ['No Data Yet'];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function showLocalData() {
|
|
243
|
+
return;
|
|
244
|
+
/*
|
|
245
|
+
const Html = [];
|
|
246
|
+
|
|
247
|
+
Html.push(`<ul class="collapsible">`);
|
|
248
|
+
Html.push('<li>')
|
|
249
|
+
Html.push (`<li class="active"><div class="collapsible-header">
|
|
250
|
+
Paired Models
|
|
251
|
+
</div>`);
|
|
252
|
+
Html.push (`<div class="collapsible-body">
|
|
253
|
+
<span>${getModelData(devices).join('')}</span>
|
|
254
|
+
</div>`);
|
|
255
|
+
Html.push ('</li><li>')
|
|
256
|
+
Html.push (`<div class="collapsible-header">
|
|
257
|
+
Paired Devices
|
|
258
|
+
</div>`);
|
|
259
|
+
Html.push (`<div class="collapsible-body">
|
|
260
|
+
<span>${getDeviceData(devices, true).join('')}</span>
|
|
261
|
+
</div>`);
|
|
262
|
+
Html.push ('</li><li>')
|
|
263
|
+
Html.push (`<div class="collapsible-header">
|
|
264
|
+
Global Options
|
|
265
|
+
</div>`);
|
|
266
|
+
Html.push (`<div class="collapsible-body">
|
|
267
|
+
<span>${getGlobalOptionData(devices).join('')}</span>
|
|
268
|
+
</div>`);
|
|
269
|
+
Html.push ('</li>')
|
|
270
|
+
Html.push (`</ul>`);
|
|
271
|
+
$('#tab-overrides').html(Html.join(''));
|
|
272
|
+
$('.collapsible').collapsible();
|
|
273
|
+
*/
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/////
|
|
277
|
+
//
|
|
278
|
+
// Section Cards
|
|
279
|
+
//
|
|
280
|
+
////
|
|
281
|
+
|
|
282
|
+
function getCard(dev) {
|
|
283
|
+
if (!dev._id) return '';
|
|
284
|
+
const title = dev.common.name,
|
|
285
|
+
id = (dev._id ? dev._id : ''),
|
|
286
|
+
type = (dev.common.type ? dev.common.type : 'unknown'),
|
|
287
|
+
type_url = (dev.common.type ? sanitizeModelParameter(dev.common.type) : 'unknown'),
|
|
288
|
+
img_src = dev.common.icon || dev.icon,
|
|
289
|
+
rooms = [],
|
|
290
|
+
isActive = (dev.common.deactivated ? false : true),
|
|
291
|
+
lang = systemLang || 'en',
|
|
292
|
+
ieee = id.replace(namespace + '.', ''),
|
|
293
|
+
isDebug = checkDebugDevice(ieee);
|
|
294
|
+
for (const r in dev.rooms) {
|
|
295
|
+
if (dev.rooms[r].hasOwnProperty(lang)) {
|
|
296
|
+
rooms.push(dev.rooms[r][lang]);
|
|
297
|
+
} else {
|
|
298
|
+
rooms.push(dev.rooms[r]);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
|
|
302
|
+
const rid = id.split('.').join('_');
|
|
303
|
+
const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type_url}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
|
|
304
|
+
const groupInfo = dev.groupNames ? `<li><span class="labelinfo">groups:</span><span>${dev.groupNames || ''}</span></li>` : '';
|
|
305
|
+
const roomInfo = rooms.length ? `<li><span class="labelinfo">rooms:</span><span>${rooms.join(',') || ''}</span></li>` : '';
|
|
306
|
+
const image = `<img src="${img_src}" width="80px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
|
|
307
|
+
nwk = (dev.info && dev.info.device) ? dev.info.device.nwk : undefined,
|
|
308
|
+
battery_cls = (isActive ? getBatteryCls(dev.battery) : ''),
|
|
309
|
+
lqi_cls = getLQICls(dev.link_quality),
|
|
310
|
+
battery = (dev.battery && isActive) ? `<div class="col tool"><i id="${rid}_battery_icon" class="material-icons ${battery_cls}">battery_std</i><div id="${rid}_battery" class="center" style="font-size:0.7em">${dev.battery}</div></div>` : '',
|
|
311
|
+
lq = (dev.link_quality > 0)
|
|
312
|
+
? `<div class="col tool"><i id="${rid}_link_quality_icon" class="material-icons ${lqi_cls}">network_check</i><div id="${rid}_link_quality" class="center" style="font-size:0.7em">${dev.link_quality}</div></div>`
|
|
313
|
+
: `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>`,
|
|
314
|
+
status = (isActive ? lq : `<div class="col tool"><i class="material-icons icon-red">cancel</i></div>`),
|
|
315
|
+
info = `<div style="min-height:88px; font-size: 0.8em" class="truncate">
|
|
316
|
+
<ul>
|
|
317
|
+
<li><span class="labelinfo">ieee:</span><span>0x${ieee}</span></li>
|
|
318
|
+
<li><span class="labelinfo">nwk:</span><span>${(nwk) ? nwk.toString() + ' (0x' + nwk.toString(16) + ')' : ''}</span></li>
|
|
319
|
+
<li><span class="labelinfo">model:</span><span>${modelUrl}</span></li>
|
|
320
|
+
${groupInfo}
|
|
321
|
+
${roomInfo}
|
|
322
|
+
</ul>
|
|
323
|
+
</div>`,
|
|
324
|
+
deactBtn = `<button name="swapactive" class="right btn-flat btn-small tooltipped" title="${(isActive ? 'Deactivate' : 'Activate')}"><i class="material-icons ${(isActive ? 'icon-green' : 'icon-red')}">power_settings_new</i></button>`,
|
|
325
|
+
debugBtn = `<button name="swapdebug" class="right btn-flat btn-small tooltipped" title="${(isDebug > -1 ? (isDebug > 0) ?'Automatic by '+debugDevices[isDebug-1]: 'Disable Debug' : 'Enable Debug')}"><i class="material-icons icon-${(isDebug > -1 ? (isDebug > 0 ? 'orange' : 'green') : 'gray')}">bug_report</i></button>`,
|
|
326
|
+
infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '';
|
|
327
|
+
|
|
328
|
+
const dashCard = getDashCard(dev);
|
|
329
|
+
const card = `<div id="${id}" class="device">
|
|
330
|
+
<div class="card hoverable flipable ${isActive ? '' : 'bg_red'}">
|
|
331
|
+
<div class="front face">${dashCard}</div>
|
|
332
|
+
<div class="back face">
|
|
333
|
+
<div class="card-content zcard">
|
|
334
|
+
<div class="flip" style="cursor: pointer">
|
|
335
|
+
<span class="top right small" style="border-radius: 50%">
|
|
336
|
+
${battery}
|
|
337
|
+
<!--${lq}-->
|
|
338
|
+
${status}
|
|
339
|
+
</span>
|
|
340
|
+
<!--/a--!>
|
|
341
|
+
<span id="dName" class="card-title truncate">${title}</span><!--${paired}--!>
|
|
342
|
+
</div>
|
|
343
|
+
<i class="left">${image}</i>
|
|
344
|
+
${info}
|
|
345
|
+
<div class="footer right-align"></div>
|
|
346
|
+
</div>
|
|
347
|
+
<div class="card-action">
|
|
348
|
+
<div class="card-reveal-buttons">
|
|
349
|
+
${infoBtn}
|
|
350
|
+
|
|
351
|
+
<span class="left fw_info"></span>
|
|
352
|
+
<button name="delete" class="right btn-flat btn-small tooltipped" title="Delete">
|
|
353
|
+
<i class="material-icons icon-red">delete</i>
|
|
354
|
+
</button>
|
|
355
|
+
<button name="edit" class="right btn-flat btn-small tooltipped" title="Edit">
|
|
356
|
+
<i class="material-icons icon-black">edit</i>
|
|
357
|
+
</button>
|
|
358
|
+
<button name="swapimage" class="right btn-flat btn-small tooltipped" title="Select Image">
|
|
359
|
+
<i class="material-icons icon-black">image</i>
|
|
360
|
+
</button>
|
|
361
|
+
<button name="reconfigure" class="right btn-flat btn-small tooltipped" title="Reconfigure">
|
|
362
|
+
<i class="material-icons icon-red">sync</i>
|
|
363
|
+
</button>
|
|
364
|
+
${deactBtn}
|
|
365
|
+
${debugBtn}
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>`;
|
|
371
|
+
return card;
|
|
372
|
+
}
|
|
115
373
|
|
|
116
374
|
function getCoordinatorCard(dev) {
|
|
117
375
|
const title = 'Zigbee Coordinator',
|
|
@@ -228,103 +486,174 @@ function getGroupCard(dev) {
|
|
|
228
486
|
return card;
|
|
229
487
|
}
|
|
230
488
|
|
|
231
|
-
function
|
|
232
|
-
|
|
233
|
-
return parameter.replace(replaceByUnderscore, '_');
|
|
489
|
+
function getDeviceCards() {
|
|
490
|
+
return $('#devices .device').not('.group');
|
|
234
491
|
}
|
|
235
492
|
|
|
236
|
-
function
|
|
237
|
-
|
|
238
|
-
|
|
493
|
+
function getDeviceCard(devId) {
|
|
494
|
+
if (devId.startsWith('0x')) {
|
|
495
|
+
devId = devId.substr(2, devId.length);
|
|
496
|
+
}
|
|
497
|
+
return $('#devices').find(`div[id='${namespace}.${devId}']`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function getDashCard(dev, groupImage, groupstatus) {
|
|
239
501
|
const title = dev.common.name,
|
|
240
|
-
id =
|
|
241
|
-
type =
|
|
242
|
-
|
|
243
|
-
|
|
502
|
+
id = dev._id,
|
|
503
|
+
type = dev.common.type,
|
|
504
|
+
img_src = (groupImage ? groupImage : dev.common.icon || dev.icon),
|
|
505
|
+
isActive = !dev.common.deactivated,
|
|
244
506
|
rooms = [],
|
|
245
|
-
|
|
246
|
-
lang = systemLang || 'en',
|
|
247
|
-
ieee = id.replace(namespace + '.', ''),
|
|
248
|
-
isDebug = checkDebugDevice(ieee);
|
|
249
|
-
for (const r in dev.rooms) {
|
|
250
|
-
if (dev.rooms[r].hasOwnProperty(lang)) {
|
|
251
|
-
rooms.push(dev.rooms[r][lang]);
|
|
252
|
-
} else {
|
|
253
|
-
rooms.push(dev.rooms[r]);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
507
|
+
lang = systemLang || 'en';
|
|
256
508
|
const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
|
|
509
|
+
const permitJoinBtn = dev.battery || dev.common.type == 'group' ? '' : `<div class="col tool"><button name="joinCard" class="waves-effect btn-small btn-flat right hoverable green"><i class="material-icons icon-green">leak_add</i></button></div>`;
|
|
510
|
+
const device_queryBtn = dev.battery || dev.common.type == 'group' ? '' : `<div class="col tool"><button name="deviceQuery" class="waves-effect btn-small btn-flat right hoverable green"><i class="material-icons icon-green">play_for_work</i></button></div>`;
|
|
257
511
|
const rid = id.split('.').join('_');
|
|
258
|
-
const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${
|
|
259
|
-
const
|
|
260
|
-
const roomInfo = rooms.length ? `<li><span class="labelinfo">rooms:</span><span>${rooms.join(',') || ''}</span></li>` : '';
|
|
261
|
-
const image = `<img src="${img_src}" width="80px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
|
|
512
|
+
const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
|
|
513
|
+
const image = `<img src="${img_src}" width="64px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
|
|
262
514
|
nwk = (dev.info && dev.info.device) ? dev.info.device.nwk : undefined,
|
|
263
|
-
battery_cls =
|
|
515
|
+
battery_cls = getBatteryCls(dev.battery),
|
|
264
516
|
lqi_cls = getLQICls(dev.link_quality),
|
|
517
|
+
unconnected_icon = (groupImage ? (groupstatus ? '<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>' : '<div class="col tool"><i class="material-icons icon-red">cancel</i></div>') :'<div class="col tool"><i class="material-icons icon-red">leak_remove</i></div>'),
|
|
265
518
|
battery = (dev.battery && isActive) ? `<div class="col tool"><i id="${rid}_battery_icon" class="material-icons ${battery_cls}">battery_std</i><div id="${rid}_battery" class="center" style="font-size:0.7em">${dev.battery}</div></div>` : '',
|
|
266
|
-
lq = (dev.link_quality > 0)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
519
|
+
lq = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_icon" class="material-icons ${lqi_cls}">network_check</i><div id="${rid}_link_quality" class="center" style="font-size:0.7em">${dev.link_quality}</div></div>` : (isActive ? unconnected_icon : ''),
|
|
520
|
+
//status = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>` : (groupImage || !isActive ? '' : `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>`),
|
|
521
|
+
//infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '',
|
|
522
|
+
idleTime = (dev.link_quality_lc > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_lc_icon" class="material-icons idletime">access_time</i><div id="${rid}_link_quality_lc" class="center" style="font-size:0.7em">${getIdleTime(dev.link_quality_lc)}</div></div>` : '';
|
|
523
|
+
const info = (dev.statesDef) ? dev.statesDef.map((stateDef) => {
|
|
524
|
+
const id = stateDef.id;
|
|
525
|
+
const sid = id.split('.').join('_');
|
|
526
|
+
let val = stateDef.val || '';
|
|
527
|
+
if (stateDef.role === 'switch' && stateDef.write) {
|
|
528
|
+
val = `<span class="switch"><label><input type="checkbox" ${(val) ? 'checked' : ''}><span class="lever"></span></label></span>`;
|
|
529
|
+
} else if (stateDef.role === 'level.dimmer' && stateDef.write) {
|
|
530
|
+
val = `<span class="range-field dash"><input type="range" min="0" max="100" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
|
|
531
|
+
} else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
|
|
532
|
+
val = `<span class="range-field dash"><input type="range" min="150" max="500" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
|
|
533
|
+
} else if (stateDef.type === 'boolean') {
|
|
534
|
+
const disabled = (stateDef.write) ? '' : 'disabled="disabled"';
|
|
535
|
+
val = `<label class="dash"><input type="checkbox" ${(val == true) ? 'checked=\'checked\'' : ''} ${disabled}/><span></span></label>`;
|
|
536
|
+
} else if (stateDef.role === 'level.color.rgb') {
|
|
537
|
+
const options = []
|
|
538
|
+
for (const key of namedColors) {
|
|
539
|
+
options.push(`<option value="${key}" ${val===key ? 'selected' : ''}>${key}</option>`);
|
|
540
|
+
}
|
|
541
|
+
val = `<select class="browser-default enum" style="color : white; background-color: grey; height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
|
|
542
|
+
} else if (stateDef.states && stateDef.write) {
|
|
543
|
+
let options;
|
|
544
|
+
if (typeof stateDef.states == 'string') {
|
|
545
|
+
const sts = stateDef.states.split(';');
|
|
546
|
+
if (sts.length < 2) return '';
|
|
547
|
+
options = sts.map((item) => {
|
|
548
|
+
const v = item.split(':');
|
|
549
|
+
return `<option value="${v[0]}" ${(val == v[0]) ? 'selected' : ''}>${v[1]}</option>`;
|
|
550
|
+
});
|
|
551
|
+
} else {
|
|
552
|
+
options = [];
|
|
553
|
+
for (const [key, value] of Object.entries(stateDef.states)) {
|
|
554
|
+
options.push(`<option value="${key}" ${(val == key) ? 'selected' : ''}>${key}</option>`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (options.length < 2) return '';
|
|
558
|
+
val = `<select class="browser-default enum" style="color : white; background-color: grey; height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
|
|
559
|
+
} else if (stateDef.write) {
|
|
560
|
+
return;
|
|
561
|
+
// val = `<span class="input-field dash value"><input class="dash value" id="${stateDef.name}" value="${val}"></input></span>`;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
val = `<span class="dash value">${val ? val : '(null)'} ${(stateDef.unit) ? stateDef.unit : ''}</span>`;
|
|
565
|
+
}
|
|
566
|
+
return `<li><span class="label dash truncate">${stateDef.name}</span><span id=${sid} oid=${id} class="state">${val}</span></li>`;
|
|
567
|
+
}).join('') : '';
|
|
568
|
+
const dashCard = `
|
|
569
|
+
<div class="card-content zcard ${isActive ? '' : 'bg_red'}">
|
|
570
|
+
<div style="cursor: pointer">
|
|
571
|
+
<span class="top right small" style="border-radius: 50%">
|
|
572
|
+
${device_queryBtn}
|
|
573
|
+
${permitJoinBtn}
|
|
574
|
+
</span>
|
|
575
|
+
<div class="flip">
|
|
576
|
+
<span class="top right small" style="border-radius: 50%">
|
|
577
|
+
${idleTime}
|
|
578
|
+
${battery}
|
|
579
|
+
${lq}
|
|
580
|
+
</span>
|
|
581
|
+
<span class="card-title truncate">${title}</span>
|
|
582
|
+
</div>
|
|
583
|
+
</div>
|
|
584
|
+
<i class="left">${image}</i>
|
|
585
|
+
<div style="min-height:88px; font-size: 0.8em; height: 130px; width: 220px; overflow-y: auto" class="truncate">
|
|
586
|
+
<ul>
|
|
587
|
+
${(isActive ? info : 'Device deactivated')}
|
|
588
|
+
</ul>
|
|
589
|
+
</div>
|
|
590
|
+
<div class="footer right-align"></div>
|
|
591
|
+
</div>`;
|
|
592
|
+
|
|
593
|
+
return dashCard;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function setDashStates(id, state) {
|
|
597
|
+
const devId = getDevId(id);
|
|
598
|
+
const dev = getDeviceByID(devId);
|
|
599
|
+
if (dev) {
|
|
600
|
+
const stateDef = dev.statesDef.find((stateDef) => stateDef.id == id);
|
|
601
|
+
if (stateDef) {
|
|
602
|
+
const sid = id.split('.').join('_');
|
|
603
|
+
if (stateDef.role === 'switch' && stateDef.write) {
|
|
604
|
+
$(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
|
|
605
|
+
} else if (stateDef.role === 'level.dimmer' && stateDef.write) {
|
|
606
|
+
$(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
|
|
607
|
+
} else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
|
|
608
|
+
$(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
|
|
609
|
+
} else if (stateDef.states && stateDef.write) {
|
|
610
|
+
$(`#${sid}`).find(`select option[value=${state.val}]`).prop('selected', true);
|
|
611
|
+
} else if (stateDef.type === 'boolean') {
|
|
612
|
+
$(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
|
|
613
|
+
} else {
|
|
614
|
+
$(`#${sid}`).find('.value').text(`${state.val} ${(stateDef.unit) ? stateDef.unit : ''}`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
282
619
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
<div class="card-reveal-buttons">
|
|
304
|
-
${infoBtn}
|
|
620
|
+
function hookControls() {
|
|
621
|
+
$('input[type=\'checkbox\']').change(function (event) {
|
|
622
|
+
const val = $(this).is(':checked');
|
|
623
|
+
const id = $(this).parents('.state').attr('oid');
|
|
624
|
+
sendToWrapper(namespace, 'setState', {id: id, val: val}, function (data) {
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
$('input[type=\'range\']').change(function (event) {
|
|
628
|
+
const val = $(this).val();
|
|
629
|
+
const id = $(this).parents('.state').attr('oid');
|
|
630
|
+
sendToWrapper(namespace, 'setState', {id: id, val: val}, function (data) {
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
$('.state select').on('change', function () {
|
|
634
|
+
const val = $(this).val();
|
|
635
|
+
const id = $(this).parents('.state').attr('oid');
|
|
636
|
+
sendToWrapper(namespace, 'setState', {id: id, val: val}, function (data) {
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
}
|
|
305
640
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
${debugBtn}
|
|
321
|
-
</div>
|
|
322
|
-
</div>
|
|
323
|
-
</div>
|
|
324
|
-
</div>
|
|
325
|
-
</div>`;
|
|
326
|
-
return card;
|
|
641
|
+
function getIdleTime(value) {
|
|
642
|
+
return (value) ? moment(new Date(value)).fromNow(true) : '';
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function updateCardTimer() {
|
|
646
|
+
if (devices) {
|
|
647
|
+
devices.forEach((dev) => {
|
|
648
|
+
const id = dev._id;
|
|
649
|
+
if (id) {
|
|
650
|
+
const rid = id.split('.').join('_');
|
|
651
|
+
$(`#${rid}_link_quality_lc`).text(getIdleTime(dev.link_quality_lc));
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
327
655
|
}
|
|
656
|
+
|
|
328
657
|
/*
|
|
329
658
|
function openReval(e, id, name){
|
|
330
659
|
const $card = $(e.target).closest('.card');
|
|
@@ -379,6 +708,17 @@ function closeReval(e, id) {
|
|
|
379
708
|
});
|
|
380
709
|
}
|
|
381
710
|
|
|
711
|
+
function showDevInfo(id) {
|
|
712
|
+
const info = genDevInfo(getDeviceByID(id));
|
|
713
|
+
$('#devinfo').html(info);
|
|
714
|
+
$('#modaldevinfo').modal('open');
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
////
|
|
718
|
+
//
|
|
719
|
+
// section Confirmations
|
|
720
|
+
//
|
|
721
|
+
////
|
|
382
722
|
function deleteConfirmation(id, name) {
|
|
383
723
|
const text = translateWord('Do you really want to delete device') + ' "' + name + '" (' + id + ')?';
|
|
384
724
|
$('#modaldelete').find('p').text(text);
|
|
@@ -402,7 +742,7 @@ function deleteNvBackupConfirmation() {
|
|
|
402
742
|
$('#modaldelete a.btn[name=\'yes\']').click(() => {
|
|
403
743
|
//const force = $('#force').prop('checked');
|
|
404
744
|
showWaitingDialog('Attempting to delete nvBackup.json', 60000);
|
|
405
|
-
|
|
745
|
+
sendToWrapper(namespace, 'deleteNVBackup', {}, function (msg) {
|
|
406
746
|
closeWaitingDialog();
|
|
407
747
|
if (msg) {
|
|
408
748
|
if (msg.error) {
|
|
@@ -444,211 +784,62 @@ function EndPointIDfromEndPoint(ep) {
|
|
|
444
784
|
|
|
445
785
|
function editName(id, name) {
|
|
446
786
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if (k && device_options.hasOwnProperty(k)) {
|
|
456
|
-
if (dev.info.mapped && dev.info.mapped.options && dev.info.mapped.options.includes(device_options[k].key))
|
|
457
|
-
availableOptions.push(device_options[k].key)
|
|
458
|
-
delete device_options[k];
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function addOption() {
|
|
463
|
-
let idx=1;
|
|
464
|
-
let key = '';
|
|
465
|
-
const optionName = $('#option_Selector').val();
|
|
466
|
-
console.warn(`option name is ${optionName}`);
|
|
467
|
-
do {
|
|
468
|
-
key = `o${idx++}`;
|
|
469
|
-
}
|
|
470
|
-
while (device_options.hasOwnProperty(key));
|
|
471
|
-
device_options[key] = { key:optionName, value:''};
|
|
472
|
-
console.warn(`device_options: ${JSON.stringify(device_options)}`);
|
|
473
|
-
idx = availableOptions.indexOf(optionName);
|
|
474
|
-
console.warn(`idx: ${idx}, ao:${JSON.stringify(availableOptions)}, on: ${optionName}`);
|
|
475
|
-
if (idx > -1) availableOptions.splice(idx, 1);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
function updateOptions(candidates) {
|
|
479
|
-
if (candidates.length > 0) {
|
|
480
|
-
$('#modaledit').find('.new_options_available').removeClass('hide');
|
|
481
|
-
list2select('#option_Selector', candidates, [], (key, val) => { return val; }, (key, val) => { return val; })
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
$('#modaledit').find('.new_options_available').addClass('hide');
|
|
485
|
-
}
|
|
486
|
-
const html_options=[];
|
|
487
|
-
|
|
488
|
-
console.warn(`option_Selector is ${JSON.stringify(device_options)}`)
|
|
489
|
-
|
|
490
|
-
for (const k in device_options) {
|
|
491
|
-
html_options.push(`<div class="row">`);
|
|
492
|
-
html_options.push(`<div class="input-field suffix col s5 m5 l5"><input disabled id="option_key_${k}" type="text" class="value" /><label for="option_key_${k}">Option</label></div>`)
|
|
493
|
-
html_options.push(`<div class="input-field suffix col s5 m5 l5"><input id="option_value_${k}" type="text" class="value" /><label for="option_value_${k}">Value</label></div>`)
|
|
494
|
-
html_options.push(`<div class="col"><a id="option_rem_${k}" class='btn' ><i class="material-icons">remove_circle</i></a></div>`);
|
|
495
|
-
html_options.push(`</div>`)
|
|
496
|
-
}
|
|
497
|
-
console.warn(`html is ${$('#modaledit').find('.options_grid').html()}`)
|
|
498
|
-
$('#modaledit').find('.options_grid').html(html_options.join(''));
|
|
499
|
-
console.warn(`html is now ${$('#modaledit').find('.options_grid').html()}`)
|
|
500
|
-
if (html_options.length > 0) {
|
|
501
|
-
$('#modaledit').find('.options_available').removeClass('hide');
|
|
502
|
-
for (const k of Object.keys(device_options)) {
|
|
503
|
-
$(`#option_key_${k}`).val(device_options[k].key);
|
|
504
|
-
$(`#option_value_${k}`).val(device_options[k].value);
|
|
505
|
-
$(`#option_rem_${k}`).unbind('click');
|
|
506
|
-
$(`#option_rem_${k}`).click(() => { removeOption(k); updateOptions(availableOptions) });
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
if (candidates.length == 0) $('#modaledit').find('.options_available').addClass('hide');
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function getOptionsFromUI(_do, _so) {
|
|
515
|
-
const _no = {};
|
|
516
|
-
let changed = false;
|
|
517
|
-
for (const k in _do) {
|
|
518
|
-
const key = $(`#option_key_${k}`).val();
|
|
519
|
-
_do[k].key = key;
|
|
520
|
-
const val = $(`#option_value_${k}`).val();
|
|
521
|
-
try {
|
|
522
|
-
_do[k].value = JSON.parse(val);
|
|
523
|
-
}
|
|
524
|
-
catch {
|
|
525
|
-
_do[k].value = val;
|
|
787
|
+
function updateGroupables(groupables) {
|
|
788
|
+
const html = [];
|
|
789
|
+
if (groupables && groupables.length > 0)
|
|
790
|
+
{
|
|
791
|
+
for (const groupable of groupables) {
|
|
792
|
+
const k = groupable.ep.ID || -1;
|
|
793
|
+
const n = groupable.epid != `unidentified` ? groupable.epid : `Endpoint ${k}`;
|
|
794
|
+
html.push(`<div class="input-field suffix col s12 m12 l12"><select id="gk_${k}" class="materialSelect" multiple><option value="1">select</option><select><label for="gk_${k}">Group membership for ${n}</label></div>`);
|
|
526
795
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
796
|
+
$('#modaledit').find('.endpoints_for_groups').html(html.join(''));
|
|
797
|
+
for (const groupable of groupables) {
|
|
798
|
+
console.warn(`list 2 select called with ${groupable.ep.ID}, groups ${JSON.stringify(groups)}, groupable ${JSON.stringify(groupable)}`);
|
|
799
|
+
list2select(`#gk_${groupable.ep.ID || -1}`, groups, groupable.memberOf || []);
|
|
530
800
|
}
|
|
531
801
|
}
|
|
532
|
-
|
|
533
|
-
if (changed) return _no;
|
|
534
|
-
return undefined;
|
|
802
|
+
return html;
|
|
535
803
|
}
|
|
536
804
|
|
|
537
805
|
|
|
538
|
-
|
|
806
|
+
const dev = devices.find((d) => d._id == id);
|
|
807
|
+
$('#modaledit').find('input[id=\'d_name\']').val(name);
|
|
808
|
+
const groupables = [];
|
|
539
809
|
if (dev && dev.info && dev.info.endpoints) {
|
|
540
810
|
for (const ep of dev.info.endpoints) {
|
|
541
811
|
if (ep.input_clusters.includes(4)) {
|
|
542
|
-
groupables.push({epid: EndPointIDfromEndPoint(ep), ep: ep, memberOf: []});
|
|
812
|
+
groupables.push({epid: EndPointIDfromEndPoint(ep), ep: ep, memberOf: dev.groups_by_ep ? dev.groups_by_ep[ep.ID] || [] : []});
|
|
543
813
|
}
|
|
544
814
|
}
|
|
545
815
|
}
|
|
546
816
|
const numEP = groupables.length;
|
|
547
|
-
const availableOptions = (dev.info.mapped ? dev.info.mapped.options.slice() || []:[]);
|
|
548
|
-
|
|
549
|
-
if (numEP > 0) {
|
|
550
|
-
$('#modaledit').find('.groups_available').removeClass('hide');
|
|
551
|
-
$('#modaledit').find('.row.epid0').addClass('hide');
|
|
552
|
-
$('#modaledit').find('.row.epid1').addClass('hide');
|
|
553
|
-
$('#modaledit').find('.row.epid2').addClass('hide');
|
|
554
|
-
$('#modaledit').find('.row.epid3').addClass('hide');
|
|
555
|
-
$('#modaledit').find('.row.epid4').addClass('hide');
|
|
556
|
-
$('#modaledit').find('.row.epid5').addClass('hide');
|
|
557
|
-
$('#modaledit').find('.row.epid6').addClass('hide');
|
|
558
|
-
// go through all the groups. Find the ones to list for each groupable
|
|
559
|
-
if (numEP == 1) {
|
|
560
|
-
$('#modaledit').find('.endpointid').addClass('hide');
|
|
561
|
-
} else {
|
|
562
|
-
$('#modaledit').find('.endpointid').removeClass('hide');
|
|
563
|
-
}
|
|
564
|
-
for (const d of devices) {
|
|
565
|
-
if (d && d.common && d.common.type == 'group') {
|
|
566
|
-
if (d.hasOwnProperty('memberinfo')) {
|
|
567
|
-
for (const member of d.memberinfo) {
|
|
568
|
-
const epid = EndPointIDfromEndPoint(member.ep);
|
|
569
|
-
for (let i = 0; i < groupables.length; i++) {
|
|
570
|
-
if (groupables[i].epid == epid) {
|
|
571
|
-
groupables[i].memberOf.push(d.native.id.replace('group_', ''));
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
console.log('groupables: ' + JSON.stringify(groupables));
|
|
579
|
-
for (let i = 0; i < groupables.length; i++) {
|
|
580
|
-
if (i > 1) {
|
|
581
|
-
$('#modaledit').find('translate.device_with_endpoint').innerHtml = name + ' ' + groupables[i].epid;
|
|
582
|
-
}
|
|
583
|
-
$('#modaledit').find('.row.epid' + i).removeClass('hide');
|
|
584
|
-
list2select('#d_groups_ep' + i, groups, groupables[i].memberOf || []);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
else
|
|
588
|
-
{
|
|
589
|
-
$('#modaledit').find('.groups_available').addClass('hide');
|
|
590
|
-
}
|
|
591
|
-
sendTo(namespace, 'getLocalConfigItems', { target:id, global:false, key:'options' }, function (msg) {
|
|
592
|
-
if (msg) {
|
|
593
|
-
if (msg.error) showMessage(msg.error, '_Error');
|
|
594
|
-
console.warn(`return is ${JSON.stringify(msg)}`)
|
|
595
|
-
Object.keys(device_options).forEach(key => delete device_options[key]);
|
|
596
|
-
Object.keys(received_options).forEach(key => delete received_options[key]);
|
|
597
|
-
if (typeof msg.options === 'object') {
|
|
598
|
-
|
|
599
|
-
let cnt = 1;
|
|
600
|
-
for (const key in msg.options)
|
|
601
|
-
{
|
|
602
|
-
const idx = availableOptions.indexOf(key);
|
|
603
|
-
console.warn(`key ${key} : index : ${idx}`);
|
|
604
|
-
if (idx > -1) availableOptions.splice(idx,1);
|
|
605
|
-
received_options[key]=msg.options[key];
|
|
606
|
-
device_options[`o${cnt}`] = { key:key, value:msg.options[key]}
|
|
607
|
-
cnt++;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
console.warn(`avo ${JSON.stringify(availableOptions)}, mapped: ${JSON.stringify(dev.info.mapped.options)}`);
|
|
611
|
-
updateOptions(availableOptions);
|
|
612
817
|
|
|
613
|
-
|
|
614
|
-
});
|
|
818
|
+
updateGroupables(groupables);
|
|
615
819
|
$('#modaledit a.btn[name=\'save\']').unbind('click');
|
|
616
|
-
$('#modaledit a.btn[name=\'add_options\']').unbind('click');
|
|
617
|
-
$('#modaledit a.btn[name=\'add_options\']').click(() => {
|
|
618
|
-
getOptionsFromUI(device_options, received_options);
|
|
619
|
-
addOption();
|
|
620
|
-
updateOptions(availableOptions)
|
|
621
|
-
});
|
|
622
820
|
$('#modaledit a.btn[name=\'save\']').click(() => {
|
|
623
821
|
const newName = $('#modaledit').find('input[id=\'d_name\']').val();
|
|
624
|
-
const
|
|
822
|
+
const groupsById = {};
|
|
625
823
|
if (groupables.length > 0) {
|
|
626
|
-
for (
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
// read device_options from UI
|
|
633
|
-
const co = getOptionsFromUI(device_options, received_options)
|
|
634
|
-
console.warn(`options have ${co ? 'changed' : 'not changed'} : ${JSON.stringify(co)} vs ${JSON.stringify(received_options)} , saving them`);
|
|
635
|
-
if (co) {
|
|
636
|
-
sendTo(namespace, 'updateLocalConfigItems', {
|
|
637
|
-
target: id,
|
|
638
|
-
global:false,
|
|
639
|
-
data: { options:co }
|
|
640
|
-
},
|
|
641
|
-
function (msg) {
|
|
642
|
-
if (msg && msg.error) showMessage(msg.error, '_Error');
|
|
643
|
-
});
|
|
824
|
+
for (const groupable of groupables) {
|
|
825
|
+
const k = groupable.ep.ID || -1;
|
|
826
|
+
const ng = $('#gk_' + k).val();
|
|
827
|
+
if (ng.toString() != groupable.memberOf.toString())
|
|
828
|
+
groupsById[k] = GenerateGroupChange(groupable.memberOf, ng);
|
|
829
|
+
}
|
|
644
830
|
}
|
|
645
|
-
updateDev(id, newName,
|
|
646
|
-
|
|
831
|
+
updateDev(id, newName, groupsById);
|
|
647
832
|
});
|
|
648
833
|
$('#modaledit').modal('open');
|
|
649
834
|
Materialize.updateTextFields();
|
|
650
835
|
}
|
|
651
836
|
|
|
837
|
+
|
|
838
|
+
////
|
|
839
|
+
//
|
|
840
|
+
//. section GroupFunctions
|
|
841
|
+
//
|
|
842
|
+
////
|
|
652
843
|
function GenerateGroupChange(oldmembers, newmembers) {
|
|
653
844
|
const grpchng = [];
|
|
654
845
|
for (const oldg of oldmembers)
|
|
@@ -659,7 +850,7 @@ function GenerateGroupChange(oldmembers, newmembers) {
|
|
|
659
850
|
}
|
|
660
851
|
|
|
661
852
|
function deleteZigbeeDevice(id, force) {
|
|
662
|
-
|
|
853
|
+
sendToWrapper(namespace, 'deleteZigbeeDevice', {id: id, force: force}, function (msg) {
|
|
663
854
|
closeWaitingDialog();
|
|
664
855
|
if (msg) {
|
|
665
856
|
if (msg.error) {
|
|
@@ -674,7 +865,7 @@ function deleteZigbeeDevice(id, force) {
|
|
|
674
865
|
|
|
675
866
|
|
|
676
867
|
function cleanDeviceStates(force) {
|
|
677
|
-
|
|
868
|
+
sendToWrapper(namespace, 'cleanDeviceStates', {force: force}, function (msg) {
|
|
678
869
|
closeWaitingDialog();
|
|
679
870
|
if (msg) {
|
|
680
871
|
if (msg.error) {
|
|
@@ -692,7 +883,7 @@ function cleanDeviceStates(force) {
|
|
|
692
883
|
|
|
693
884
|
function renameDevice(id, name) {
|
|
694
885
|
showMessage('rename device with ' + id + ' and ' + name, _('Error'));
|
|
695
|
-
|
|
886
|
+
sendToWrapper(namespace, 'renameDevice', {id: id, name: name}, function (msg) {
|
|
696
887
|
if (msg) {
|
|
697
888
|
if (msg.error) {
|
|
698
889
|
showMessage(msg.error, _('Error'));
|
|
@@ -704,7 +895,6 @@ function renameDevice(id, name) {
|
|
|
704
895
|
}
|
|
705
896
|
|
|
706
897
|
function showDevices() {
|
|
707
|
-
console.warn('show Devices called')
|
|
708
898
|
let html = '';
|
|
709
899
|
let hasCoordinator = false;
|
|
710
900
|
const lang = systemLang || 'en';
|
|
@@ -796,17 +986,20 @@ function showDevices() {
|
|
|
796
986
|
$('.card.flipable').toggleClass('flipped');
|
|
797
987
|
});
|
|
798
988
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
989
|
+
const element = $('#devices');
|
|
990
|
+
|
|
991
|
+
if (element) {
|
|
992
|
+
shuffleInstance = devices && devices.length ? new Shuffle(element, {
|
|
993
|
+
itemSelector: '.device',
|
|
994
|
+
sizer: '.js-shuffle-sizer',
|
|
995
|
+
}) : undefined;
|
|
996
|
+
doFilter();
|
|
997
|
+
}
|
|
804
998
|
|
|
805
999
|
const getDevName = function (dev_block) {
|
|
806
1000
|
return dev_block.find('#dName').text();
|
|
807
1001
|
};
|
|
808
1002
|
const getDevId = function (dev_block) {
|
|
809
|
-
console.warn(`getDevId called with ${JSON.stringify(dev_block)}`)
|
|
810
1003
|
return dev_block.attr('id');
|
|
811
1004
|
};
|
|
812
1005
|
$('.card-reveal-buttons button[name=\'delete\']').click(function () {
|
|
@@ -856,7 +1049,7 @@ function showDevices() {
|
|
|
856
1049
|
//console.log(data);
|
|
857
1050
|
}); });
|
|
858
1051
|
$('#modalpairing a.btn[name=\'extendpairing\']').click(function () {
|
|
859
|
-
|
|
1052
|
+
openNetwork();
|
|
860
1053
|
});
|
|
861
1054
|
$('#modalpairing a.btn[name=\'endpairing\']').click(function () {
|
|
862
1055
|
stopPairing();
|
|
@@ -874,7 +1067,7 @@ function showDevices() {
|
|
|
874
1067
|
});
|
|
875
1068
|
$('.card-reveal-buttons button[name=\'reconfigure\']').click(function () {
|
|
876
1069
|
const dev_block = $(this).parents('div.device');
|
|
877
|
-
|
|
1070
|
+
reconfigureConfirmation(getDevId(dev_block));
|
|
878
1071
|
});
|
|
879
1072
|
$('.card-reveal-buttons button[name=\'swapactive\']').click(function () {
|
|
880
1073
|
const dev_block = $(this).parents('div.device');
|
|
@@ -886,7 +1079,7 @@ function showDevices() {
|
|
|
886
1079
|
}
|
|
887
1080
|
|
|
888
1081
|
function downloadIcons() {
|
|
889
|
-
|
|
1082
|
+
sendToWrapper(namespace, 'downloadIcons', {}, function (msg) {
|
|
890
1083
|
if (msg && msg.msg) {
|
|
891
1084
|
showMessage(msg.msg, _('Result'));
|
|
892
1085
|
}
|
|
@@ -915,9 +1108,8 @@ function checkFwUpdate() {
|
|
|
915
1108
|
fwInfoNode.html(createBtn('system_update', 'Click to start firmware update', false));
|
|
916
1109
|
$(fwInfoNode).find('button[name=\'fw_update\']').click(() => {
|
|
917
1110
|
fwInfoNode.html(createBtn('check_circle', 'Firmware update started, check progress in logs.', true, 'icon-blue'));
|
|
918
|
-
|
|
1111
|
+
sendToWrapper(namespace, 'startOta', {devId: devId}, (msg) => {
|
|
919
1112
|
fwInfoNode.html(createBtn('check_circle', 'Finished, see logs.', true));
|
|
920
|
-
console.log(msg);
|
|
921
1113
|
});
|
|
922
1114
|
});
|
|
923
1115
|
} else if (msg.status == 'not_available') {
|
|
@@ -937,13 +1129,13 @@ function checkFwUpdate() {
|
|
|
937
1129
|
}
|
|
938
1130
|
const devId = getDevId(devIdAttr);
|
|
939
1131
|
getFwInfoNode(deviceCard).html('<span class="left" style="padding-top:8px">checking...</span>');
|
|
940
|
-
|
|
1132
|
+
sendToWrapper(namespace, 'checkOtaAvail', {devId: devId}, callback);
|
|
941
1133
|
}
|
|
942
1134
|
}
|
|
943
1135
|
|
|
944
1136
|
function letsPairingWithCode(code) {
|
|
945
1137
|
messages = [];
|
|
946
|
-
|
|
1138
|
+
sendToWrapper(namespace, 'letsPairing', {code: code, stop:false}, function (msg) {
|
|
947
1139
|
if (msg && msg.error) {
|
|
948
1140
|
showMessage(msg.error, _('Error'));
|
|
949
1141
|
}
|
|
@@ -953,18 +1145,19 @@ function letsPairingWithCode(code) {
|
|
|
953
1145
|
});
|
|
954
1146
|
}
|
|
955
1147
|
|
|
956
|
-
function
|
|
1148
|
+
function openNetwork() {
|
|
957
1149
|
messages = [];
|
|
958
|
-
|
|
1150
|
+
sendToWrapper(namespace, 'letsPairing', {stop:false}, function (msg) {
|
|
959
1151
|
if (msg && msg.error) {
|
|
960
1152
|
showMessage(msg.error, _('Error'));
|
|
961
1153
|
}
|
|
1154
|
+
else showPairingProcess();
|
|
962
1155
|
});
|
|
963
1156
|
}
|
|
964
1157
|
|
|
965
1158
|
function stopPairing() {
|
|
966
1159
|
messages = [];
|
|
967
|
-
|
|
1160
|
+
sendToWrapper(namespace, 'letsPairing', {stop:true}, function (msg) {
|
|
968
1161
|
if (msg && msg.error) {
|
|
969
1162
|
showMessage(msg.error, _('Error'));
|
|
970
1163
|
}
|
|
@@ -973,7 +1166,7 @@ function stopPairing() {
|
|
|
973
1166
|
|
|
974
1167
|
function touchlinkReset() {
|
|
975
1168
|
messages = [];
|
|
976
|
-
|
|
1169
|
+
sendToWrapper(namespace, 'touchlinkReset', {}, function (msg) {
|
|
977
1170
|
if (msg && msg.error) {
|
|
978
1171
|
showMessage(msg.error, _('Error'));
|
|
979
1172
|
}
|
|
@@ -982,7 +1175,7 @@ function touchlinkReset() {
|
|
|
982
1175
|
|
|
983
1176
|
function joinProcess(devId) {
|
|
984
1177
|
messages = [];
|
|
985
|
-
|
|
1178
|
+
sendToWrapper(namespace, 'letsPairing', {id: devId, stop:false}, function (msg) {
|
|
986
1179
|
if (msg && msg.error) {
|
|
987
1180
|
showMessage(msg.error, _('Error'));
|
|
988
1181
|
}
|
|
@@ -990,10 +1183,8 @@ function joinProcess(devId) {
|
|
|
990
1183
|
}
|
|
991
1184
|
|
|
992
1185
|
function getCoordinatorInfo() {
|
|
993
|
-
|
|
994
|
-
sendTo(namespace, 'getCoordinatorInfo', {}, function (msg) {
|
|
1186
|
+
sendToWrapper(namespace, 'getCoordinatorInfo', {}, function (msg) {
|
|
995
1187
|
if (msg) {
|
|
996
|
-
console.warn(JSON.stringify(msg))
|
|
997
1188
|
if (msg.error) {
|
|
998
1189
|
errorData.push(msg.error);
|
|
999
1190
|
delete msg.error;
|
|
@@ -1020,9 +1211,10 @@ function checkDebugDevice(id) {
|
|
|
1020
1211
|
}
|
|
1021
1212
|
return -1;
|
|
1022
1213
|
}
|
|
1214
|
+
|
|
1023
1215
|
async function toggleDebugDevice(id) {
|
|
1024
|
-
|
|
1025
|
-
|
|
1216
|
+
sendToWrapper(namespace, 'setDeviceDebug', {id:id}, function (msg) {
|
|
1217
|
+
sendToWrapper(namespace, 'getDebugDevices', {}, function(msg) {
|
|
1026
1218
|
if (msg && typeof (msg.debugDevices == 'array')) {
|
|
1027
1219
|
debugDevices = msg.debugDevices;
|
|
1028
1220
|
}
|
|
@@ -1034,46 +1226,136 @@ async function toggleDebugDevice(id) {
|
|
|
1034
1226
|
}
|
|
1035
1227
|
|
|
1036
1228
|
function updateLocalConfigItems(device, data, global) {
|
|
1037
|
-
|
|
1229
|
+
sendToWrapper(namespace, 'updateLocalConfigItems', {target: device, data:data, global:global}, function(msg) {
|
|
1038
1230
|
if (msg && msg.hasOwnProperty.error) {
|
|
1039
1231
|
showMessage(msg.error, _('Error'));
|
|
1040
1232
|
}
|
|
1041
|
-
getDevices();
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1233
|
+
getDevices();
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
async function selectImageOverride(id) {
|
|
1238
|
+
|
|
1239
|
+
// start local functions
|
|
1240
|
+
function removeOption(k) {
|
|
1241
|
+
if (k && device_options.hasOwnProperty(k)) {
|
|
1242
|
+
if (dev.info.mapped && dev.info.mapped.options && dev.info.mapped.options.includes(device_options[k].key))
|
|
1243
|
+
availableOptions.push(device_options[k].key)
|
|
1244
|
+
delete device_options[k];
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
function addOption() {
|
|
1249
|
+
let idx=1;
|
|
1250
|
+
let key = '';
|
|
1251
|
+
const optionName = $('#option_Selector').val();
|
|
1252
|
+
do {
|
|
1253
|
+
key = `o${idx++}`;
|
|
1254
|
+
}
|
|
1255
|
+
while (device_options.hasOwnProperty(key));
|
|
1256
|
+
device_options[key] = { key:optionName, value:''};
|
|
1257
|
+
idx = availableOptions.indexOf(optionName);
|
|
1258
|
+
if (idx > -1) availableOptions.splice(idx, 1);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
function updateOptions(candidates) {
|
|
1262
|
+
if (candidates.length > 0) {
|
|
1263
|
+
$('#chooseimage').find('.new_options_available').removeClass('hide');
|
|
1264
|
+
list2select('#option_Selector', candidates, [], (key, val) => { return val; }, (key, val) => { return val; })
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
$('#chooseimage').find('.new_options_available').addClass('hide');
|
|
1268
|
+
}
|
|
1269
|
+
const html_options=[];
|
|
1270
|
+
|
|
1271
|
+
for (const k of Object.keys(device_options)) {
|
|
1272
|
+
html_options.push(`<div class="row">`);
|
|
1273
|
+
html_options.push(`<div class="input-field suffix col s5 m5 l5"><input disabled id="option_key_${k}" type="text" class="value" /><label for="option_key_${k}">Option</label></div>`)
|
|
1274
|
+
html_options.push(`<div class="input-field suffix col s5 m5 l5"><input id="option_value_${k}" type="text" class="value" /><label for="option_value_${k}">Value</label></div>`)
|
|
1275
|
+
html_options.push(`<div class="col"><a id="option_rem_${k}" class="btn-large round red" ><i class="material-icons icon-red">remove_circle</i></a></div>`);
|
|
1276
|
+
html_options.push(`</div>`)
|
|
1277
|
+
}
|
|
1278
|
+
$('#chooseimage').find('.options_grid').html(html_options.join(''));
|
|
1279
|
+
if (html_options.length > 0) {
|
|
1280
|
+
for (const k of Object.keys(device_options)) {
|
|
1281
|
+
$(`#option_key_${k}`).val(device_options[k].key);
|
|
1282
|
+
$(`#option_value_${k}`).val(device_options[k].value);
|
|
1283
|
+
$(`#option_rem_${k}`).unbind('click');
|
|
1284
|
+
$(`#option_rem_${k}`).click(() => { removeOption(k); updateOptions(availableOptions) });
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
function getOptionsFromUI(_do, _so) {
|
|
1290
|
+
const _no = {};
|
|
1291
|
+
let changed = false;
|
|
1292
|
+
for (const k of Object.keys(_do)) {
|
|
1293
|
+
const key = $(`#option_key_${k}`).val();
|
|
1294
|
+
_do[k].key = key;
|
|
1295
|
+
const val = $(`#option_value_${k}`).val();
|
|
1296
|
+
try {
|
|
1297
|
+
_do[k].value = JSON.parse(val);
|
|
1298
|
+
}
|
|
1299
|
+
catch {
|
|
1300
|
+
_do[k].value = val;
|
|
1301
|
+
}
|
|
1302
|
+
if (device_options[k].key.length > 0) {
|
|
1303
|
+
_no[key] = device_options[k].value;
|
|
1304
|
+
changed |= _no[key] != _so[key];
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
changed |= (Object.keys(_no).length != Object.keys(_so).length);
|
|
1308
|
+
console.warn(`${changed} : ${JSON.stringify(_do)} - ${JSON.stringify(_no)}`)
|
|
1309
|
+
if (changed) return _no;
|
|
1310
|
+
return undefined;
|
|
1311
|
+
}
|
|
1044
1312
|
|
|
1313
|
+
function updateImageSelection(dev, imagedata) {
|
|
1314
|
+
const default_icon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
|
|
1315
|
+
if (dev.legacyIcon) imagedata.unshift( { file:dev.legacyIcon, name:'legacy', data:dev.legacyIcon});
|
|
1316
|
+
imagedata.unshift( { file:'none', name:'default', data:default_icon});
|
|
1317
|
+
imagedata.unshift( { file:'current', name:'current', data:dev.common.icon || dev.icon});
|
|
1318
|
+
|
|
1319
|
+
list2select('#images', imagedata, selectItems,
|
|
1320
|
+
function (key, image) {
|
|
1321
|
+
return image.name
|
|
1322
|
+
},
|
|
1323
|
+
function (key, image) {
|
|
1324
|
+
return image.file;
|
|
1325
|
+
},
|
|
1326
|
+
function (key, image) {
|
|
1327
|
+
if (image.isBase64) {
|
|
1328
|
+
return `data-icon="data:image/png; base64, ${image.data}"`;
|
|
1329
|
+
} else {
|
|
1330
|
+
return `data-icon="${image.data}"`;
|
|
1331
|
+
}
|
|
1332
|
+
},
|
|
1333
|
+
);
|
|
1334
|
+
|
|
1335
|
+
}
|
|
1336
|
+
// end local functions
|
|
1337
|
+
const device_options = {};
|
|
1338
|
+
const received_options = {};
|
|
1045
1339
|
|
|
1046
|
-
async function selectImageOverride(id) {
|
|
1047
1340
|
const dev = devices.find((d) => d._id == id);
|
|
1341
|
+
const availableOptions = (dev.info.mapped ? dev.info.mapped.options.slice() || []:[]);
|
|
1048
1342
|
const imghtml = `<img src="${dev.common.icon || dev.icon}" width="80px">`
|
|
1049
1343
|
//console.error(imghtml)
|
|
1050
1344
|
const selectItems= [''];
|
|
1051
1345
|
$('#chooseimage').find('input[id=\'d_name\']').val(dev.common.name);
|
|
1052
1346
|
$('#chooseimage').find('.currentIcon').html(imghtml);
|
|
1347
|
+
$('#option_add_1084').unbind('click');
|
|
1348
|
+
$('#option_add_1084').click(() => {
|
|
1349
|
+
getOptionsFromUI(device_options, received_options);
|
|
1350
|
+
addOption();
|
|
1351
|
+
updateOptions(availableOptions)
|
|
1352
|
+
});
|
|
1053
1353
|
|
|
1054
|
-
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
sendToWrapper(namespace, 'getLocalImages', {}, function(msg) {
|
|
1055
1357
|
if (msg && msg.imageData) {
|
|
1056
|
-
|
|
1057
|
-
const default_icon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
|
|
1058
|
-
if (dev.legacyIcon) imagedata.unshift( { file:dev.legacyIcon, name:'legacy', data:dev.legacyIcon});
|
|
1059
|
-
imagedata.unshift( { file:'none', name:'default', data:default_icon});
|
|
1060
|
-
imagedata.unshift( { file:'current', name:'current', data:dev.common.icon || dev.icon});
|
|
1061
|
-
|
|
1062
|
-
list2select('#images', imagedata, selectItems,
|
|
1063
|
-
function (key, image) {
|
|
1064
|
-
return image.name
|
|
1065
|
-
},
|
|
1066
|
-
function (key, image) {
|
|
1067
|
-
return image.file;
|
|
1068
|
-
},
|
|
1069
|
-
function (key, image) {
|
|
1070
|
-
if (image.isBase64) {
|
|
1071
|
-
return `data-icon="data:image/png; base64, ${image.data}"`;
|
|
1072
|
-
} else {
|
|
1073
|
-
return `data-icon="${image.data}"`;
|
|
1074
|
-
}
|
|
1075
|
-
},
|
|
1076
|
-
);
|
|
1358
|
+
updateImageSelection(dev, msg.imageData);
|
|
1077
1359
|
|
|
1078
1360
|
$('#chooseimage a.btn[name=\'save\']').unbind('click');
|
|
1079
1361
|
$('#chooseimage a.btn[name=\'save\']').click(() => {
|
|
@@ -1083,14 +1365,36 @@ async function selectImageOverride(id) {
|
|
|
1083
1365
|
const data = {};
|
|
1084
1366
|
if (image != 'current') data.icon= image;
|
|
1085
1367
|
if (name != dev.common.name) data.name = name;
|
|
1368
|
+
data.options = getOptionsFromUI(device_options, received_options)
|
|
1369
|
+
|
|
1086
1370
|
updateLocalConfigItems(id, data, global);
|
|
1087
1371
|
});
|
|
1088
|
-
|
|
1089
|
-
|
|
1372
|
+
sendToWrapper(namespace, 'getLocalConfigItems', { target:id, global:false, key:'options' }, function (msg) {
|
|
1373
|
+
if (msg) {
|
|
1374
|
+
if (msg.error) showMessage(msg.error, '_Error');
|
|
1375
|
+
Object.keys(device_options).forEach(key => delete device_options[key]);
|
|
1376
|
+
Object.keys(received_options).forEach(key => delete received_options[key]);
|
|
1377
|
+
if (typeof msg.options === 'object') {
|
|
1378
|
+
let cnt = 1;
|
|
1379
|
+
for (const key in msg.options)
|
|
1380
|
+
{
|
|
1381
|
+
const idx = availableOptions.indexOf(key);
|
|
1382
|
+
console.warn(`key ${key} : index : ${idx}`);
|
|
1383
|
+
if (idx > -1) availableOptions.splice(idx,1);
|
|
1384
|
+
received_options[key]=msg.options[key];
|
|
1385
|
+
device_options[`o${cnt}`] = { key:key, value:msg.options[key]}
|
|
1386
|
+
cnt++;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
updateOptions(availableOptions);
|
|
1390
|
+
} else showMessage('callback without message');
|
|
1391
|
+
$('#chooseimage').modal('open');
|
|
1392
|
+
Materialize.updateTextFields();
|
|
1393
|
+
});
|
|
1090
1394
|
}
|
|
1091
1395
|
});
|
|
1092
|
-
}
|
|
1093
1396
|
|
|
1397
|
+
}
|
|
1094
1398
|
|
|
1095
1399
|
function safestring(val) {
|
|
1096
1400
|
const t = typeof val;
|
|
@@ -1100,6 +1404,11 @@ function safestring(val) {
|
|
|
1100
1404
|
return val;
|
|
1101
1405
|
}
|
|
1102
1406
|
|
|
1407
|
+
////
|
|
1408
|
+
//
|
|
1409
|
+
//. section DebugUI
|
|
1410
|
+
//
|
|
1411
|
+
////
|
|
1103
1412
|
function fne(item) {
|
|
1104
1413
|
const rv = [];
|
|
1105
1414
|
if (item.flags) {
|
|
@@ -1146,13 +1455,12 @@ function HtmlFromInDebugMessages(messages, devID, filter) {
|
|
|
1146
1455
|
isodd=!isodd;
|
|
1147
1456
|
}
|
|
1148
1457
|
}
|
|
1149
|
-
const ifbutton = `<a id="i_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="
|
|
1150
|
-
const ofbutton = `<a id="hi_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="
|
|
1458
|
+
const ifbutton = `<a id="i_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Filter debug messages"><i class="material-icons large">${dbgMsgfilter.has('i_'+devID) ? 'filter_list' : 'format_align_justify' }</i></a>`
|
|
1459
|
+
const ofbutton = `<a id="hi_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Hide debug messages"><i class="material-icons large">${dbgMsghide.has('i_'+devID) ? 'unfold_more' : 'unfold_less' }</i></a>`
|
|
1151
1460
|
const dataHide = dbgMsgfilter.has('hi_'+devID) ? 'Data hidden' : ' ';
|
|
1152
1461
|
return {html:`<thead id="dbgtable"><tr><td> </td><td>Incoming messages</td><td> </td><td> </td><td>${dataHide}</td><td>${ifbutton}</td><td>${ofbutton}</td></tr><tr><td>ID</td><td>Zigbee Payload</td><td> </td><td>State Payload</td><td>ID</td><td>value</td><td>Flags</td></tr></thead><tbody>${Html.join('')}</tbody>`, buttonList };
|
|
1153
1462
|
}
|
|
1154
1463
|
|
|
1155
|
-
|
|
1156
1464
|
function HtmlFromOutDebugMessages(messages, devID, filter) {
|
|
1157
1465
|
const Html = [];
|
|
1158
1466
|
const filterSet = new Set();
|
|
@@ -1188,13 +1496,12 @@ function HtmlFromOutDebugMessages(messages, devID, filter) {
|
|
|
1188
1496
|
isodd=!isodd;
|
|
1189
1497
|
}
|
|
1190
1498
|
}
|
|
1191
|
-
const ifbutton = `<a id="o_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="
|
|
1192
|
-
const ofbutton = `<a id="ho_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="
|
|
1499
|
+
const ifbutton = `<a id="o_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Filter debug messages"><i class="material-icons large">${dbgMsgfilter.has('o_'+devID) ? 'filter_list' : 'format_align_justify' }</i></a>`
|
|
1500
|
+
const ofbutton = `<a id="ho_${devID}" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Hide debug messages"><i class="material-icons large">${dbgMsghide.has('o_'+devID) ? 'unfold_more' : 'unfold_less'}</i></a>`
|
|
1193
1501
|
const dataHide = dbgMsgfilter.has('ho_'+devID) ? 'Data hidden' : ' ';
|
|
1194
1502
|
return { html:`<thead id="dbgtable"><tr><td> </td><td>Outgoing messages</td><td> </td><td> </td><td>${dataHide}</td><td>${ifbutton}</td><td>${ofbutton}</td></tr><tr><td>ID</td><td>Zigbee Payload</td><td>EP</td><td>ID</td><td>value</td><td>State Payload</td><td>Flags</td></tr></thead><tbody>${Html.join('')}</tbody>`, buttonList};
|
|
1195
1503
|
}
|
|
1196
1504
|
|
|
1197
|
-
|
|
1198
1505
|
function displayDebugMessages(msg) {
|
|
1199
1506
|
const buttonNames = [];
|
|
1200
1507
|
const idButtons = [];
|
|
@@ -1204,9 +1511,9 @@ function displayDebugMessages(msg) {
|
|
|
1204
1511
|
const keylength = keys.length;
|
|
1205
1512
|
const Html = [];
|
|
1206
1513
|
const button = `<a id="e_all" class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT" title="Update debug messages"><i class="material-icons large">sync_problem</i></a>`;
|
|
1207
|
-
const dbutton = `<a id="d_all" class="btn-floating waves-effect waves-light red tooltipped center-align hoverable translateT" title="
|
|
1208
|
-
const fbutton = `<a id="f_all" class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT" title="
|
|
1209
|
-
const hbutton = `<a id="h_all" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="
|
|
1514
|
+
const dbutton = `<a id="d_all" class="btn-floating waves-effect waves-light red tooltipped center-align hoverable translateT" title="Delete debug messages"><i class="material-icons icon-yellowlarge">delete_forever</i></a>`;
|
|
1515
|
+
const fbutton = `<a id="f_all" class="btn-floating waves-effect waves-light green tooltipped center-align hoverable translateT" title="Filter debug messages"><i class="material-icons large">${dbgMsgfilter.size != 0 ? 'filter_list' : 'format_align_justify' }</i></a>`;
|
|
1516
|
+
const hbutton = `<a id="h_all" class="btn-floating waves-effect waves-light blue tooltipped center-align hoverable translateT" title="Hide debug messages"><i class="material-icons large">${dbgMsghide.size != 0 ? 'unfold_more' : 'unfold_less'}</i></a>`;
|
|
1210
1517
|
const logbutton = `<a id="l_all" class="btn-floating waves-effect waves-light ${debugInLog ? 'green' : 'red'} tooltipped center-align hoverable translateT" title="Log messages"><i class="material-icons large">${debugInLog ? 'speaker_notes' : 'speaker_notes_off'}</i></a>`;
|
|
1211
1518
|
Html.push(`<li><table><thead id="dbgtable"><tr><td>${logbutton}</td><td colspan="3">Debug information by device</td><td>${fbutton}</td><td>${hbutton}</td><td>${button}</td><td>${dbutton}</td></tr></thead><tbody>`);
|
|
1212
1519
|
if (!keylength) {
|
|
@@ -1299,7 +1606,6 @@ function displayDebugMessages(msg) {
|
|
|
1299
1606
|
});
|
|
1300
1607
|
}
|
|
1301
1608
|
for (const b of idButtons) {
|
|
1302
|
-
console.warn(`trying to add link to button ${b}`);
|
|
1303
1609
|
$(`#lx_${b}`).click(function() { showMessageList(b)});
|
|
1304
1610
|
}
|
|
1305
1611
|
}
|
|
@@ -1344,48 +1650,38 @@ function showNamedMessages(messages, title, icon, timestamp) {
|
|
|
1344
1650
|
}
|
|
1345
1651
|
|
|
1346
1652
|
function showMessageList(msgId) {
|
|
1347
|
-
console.warn(`trying to show messages for ${msgId}`);
|
|
1348
|
-
console.warn(JSON.stringify(debugMessages));
|
|
1349
1653
|
for (const devId of Object.keys(debugMessages.byId)) {
|
|
1350
1654
|
for (const id of debugMessages.byId[devId].IN) {
|
|
1351
1655
|
if (id.dataID == msgId) {
|
|
1352
|
-
console.warn(`showing messages for ${id.type} ${devId}`);
|
|
1353
1656
|
showNamedMessages(id.messages, `Messages from ${new Date(msgId).toLocaleTimeString()} for device ${devId}`);
|
|
1354
1657
|
return;
|
|
1355
1658
|
}
|
|
1356
1659
|
}
|
|
1357
1660
|
for (const id of debugMessages.byId[devId].OUT) {
|
|
1358
1661
|
if (id.dataID == msgId) {
|
|
1359
|
-
console.warn(`showing messages for ${msgId}`);
|
|
1360
1662
|
showNamedMessages(id.messages, `Messages from ${new Date(msgId).toLocaleTimeString()} for device ${devId}`);
|
|
1361
1663
|
return;
|
|
1362
1664
|
}
|
|
1363
1665
|
}
|
|
1364
1666
|
}
|
|
1365
|
-
console.warn(`nothing to show`);
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
1667
|
}
|
|
1369
1668
|
|
|
1370
1669
|
function getDebugMessages(deleteBeforeRead, deleteSelected) {
|
|
1371
|
-
|
|
1670
|
+
sendToWrapper(namespace, 'getDebugMessages', { inlog: debugInLog, del:deleteBeforeRead ? deleteSelected : '' }, function(msg) {
|
|
1372
1671
|
debugMessages = msg;
|
|
1373
1672
|
if (msg) displayDebugMessages(debugMessages)
|
|
1374
1673
|
})
|
|
1375
1674
|
}
|
|
1376
1675
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1676
|
+
////
|
|
1677
|
+
//
|
|
1678
|
+
//. section getDataFromAdapter
|
|
1679
|
+
//
|
|
1680
|
+
////
|
|
1381
1681
|
function getDevices() {
|
|
1382
|
-
console.warn('getDevices called')
|
|
1383
|
-
|
|
1384
1682
|
function sendForData() {
|
|
1385
|
-
|
|
1386
|
-
console.warn(`getCoordinatorInfo returned ${JSON.stringify(msg)}`)
|
|
1683
|
+
sendToWrapper(namespace, 'getCoordinatorInfo', {}, function (msg) {
|
|
1387
1684
|
if (msg) {
|
|
1388
|
-
console.warn(JSON.stringify(msg))
|
|
1389
1685
|
if (msg.error) {
|
|
1390
1686
|
errorData.push(msg.error);
|
|
1391
1687
|
delete msg.error;
|
|
@@ -1396,7 +1692,7 @@ function getDevices() {
|
|
|
1396
1692
|
coordinatorinfo = msg;
|
|
1397
1693
|
updateStartButton()
|
|
1398
1694
|
}
|
|
1399
|
-
|
|
1695
|
+
sendToWrapper(namespace, 'getDevices', {}, function (msg) {
|
|
1400
1696
|
if (msg) {
|
|
1401
1697
|
devices = msg.devices ? msg.devices : [];
|
|
1402
1698
|
// check if stashed error messages are sent alongside
|
|
@@ -1416,13 +1712,11 @@ function getDevices() {
|
|
|
1416
1712
|
//check if debug messages are sent alongside
|
|
1417
1713
|
if (msg && typeof (msg.debugDevices == 'array')) {
|
|
1418
1714
|
debugDevices = msg.debugDevices;
|
|
1419
|
-
console.warn('debug devices is sent')
|
|
1420
1715
|
}
|
|
1421
1716
|
else
|
|
1422
1717
|
debugDevices = [];
|
|
1423
1718
|
if (debugMessages.byId) {
|
|
1424
1719
|
newDebugMessages = true;
|
|
1425
|
-
console.warn('having debug messages');
|
|
1426
1720
|
debugMessages.byId = msg;
|
|
1427
1721
|
if (msg) displayDebugMessages(debugMessages)
|
|
1428
1722
|
}
|
|
@@ -1430,19 +1724,18 @@ function getDevices() {
|
|
|
1430
1724
|
if (msg.error) {
|
|
1431
1725
|
errorData.push(msg.error);
|
|
1432
1726
|
isHerdsmanRunning = false;
|
|
1433
|
-
updateStartButton();
|
|
1434
|
-
showDevices();
|
|
1435
1727
|
} else {
|
|
1436
1728
|
isHerdsmanRunning = true;
|
|
1437
|
-
updateStartButton();
|
|
1438
|
-
showDevices();
|
|
1439
1729
|
if (!newDebugMessages) {
|
|
1440
|
-
console.warn('getting debug messages');
|
|
1441
1730
|
getDebugMessages();
|
|
1442
1731
|
}
|
|
1443
1732
|
//getExclude();
|
|
1444
1733
|
getBinding();
|
|
1445
1734
|
}
|
|
1735
|
+
updateStartButton();
|
|
1736
|
+
showDevices();
|
|
1737
|
+
showLocalData();
|
|
1738
|
+
UpdateAdapterAlive(true)
|
|
1446
1739
|
}
|
|
1447
1740
|
});
|
|
1448
1741
|
});
|
|
@@ -1450,7 +1743,6 @@ function getDevices() {
|
|
|
1450
1743
|
|
|
1451
1744
|
if (lockout.timeoutid) {
|
|
1452
1745
|
clearTimeout(lockout.timeoutid);
|
|
1453
|
-
console.warn('clearing getDevices timeout')
|
|
1454
1746
|
}
|
|
1455
1747
|
|
|
1456
1748
|
setTimeout(() => {
|
|
@@ -1462,28 +1754,18 @@ function getDevices() {
|
|
|
1462
1754
|
}
|
|
1463
1755
|
|
|
1464
1756
|
function getNamedColors() {
|
|
1465
|
-
|
|
1757
|
+
sendToWrapper(namespace, 'getNamedColors', {}, function(msg) {
|
|
1466
1758
|
if (msg && typeof msg.colors) {
|
|
1467
1759
|
namedColors = msg.colors;
|
|
1468
1760
|
}
|
|
1469
1761
|
});
|
|
1470
1762
|
}
|
|
1471
1763
|
|
|
1472
|
-
function getDeviceCards() {
|
|
1473
|
-
return $('#devices .device').not('.group');
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
function getDeviceCard(devId) {
|
|
1477
|
-
if (devId.startsWith('0x')) {
|
|
1478
|
-
devId = devId.substr(2, devId.length);
|
|
1479
|
-
}
|
|
1480
|
-
return $('#devices').find(`div[id='${namespace}.${devId}']`);
|
|
1481
|
-
}
|
|
1482
1764
|
|
|
1483
1765
|
function getMap(rebuild) {
|
|
1484
1766
|
$('#refresh').addClass('disabled');
|
|
1485
1767
|
if (isHerdsmanRunning) {
|
|
1486
|
-
|
|
1768
|
+
sendToWrapper(namespace, 'getMap', { forcebuild:rebuild}, function (msg) {
|
|
1487
1769
|
$('#refresh').removeClass('disabled');
|
|
1488
1770
|
if (msg) {
|
|
1489
1771
|
if (msg.error) {
|
|
@@ -1505,27 +1787,27 @@ function getMap(rebuild) {
|
|
|
1505
1787
|
else showMessage('Unable to generate map, the zigbee subsystem is inactive', 'Map generation error');
|
|
1506
1788
|
}
|
|
1507
1789
|
|
|
1508
|
-
function getRandomExtPanID()
|
|
1509
|
-
{
|
|
1510
|
-
const bytes = [];
|
|
1511
|
-
for (let i = 0;i<16;i++) {
|
|
1512
|
-
bytes.push(Math.floor(Math.random() * 16).toString(16));
|
|
1513
|
-
}
|
|
1514
|
-
return bytes.join('');
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
function getRandomChannel()
|
|
1518
|
-
{
|
|
1519
|
-
const channels = [11,15,20,25]
|
|
1520
|
-
return channels[Math.floor(Math.random() * 4)];
|
|
1521
|
-
}
|
|
1522
1790
|
|
|
1523
1791
|
|
|
1524
1792
|
|
|
1525
1793
|
// the function loadSettings has to exist ...
|
|
1526
1794
|
|
|
1527
1795
|
function load(settings, onChange) {
|
|
1528
|
-
|
|
1796
|
+
function getRandomExtPanID()
|
|
1797
|
+
{
|
|
1798
|
+
const bytes = [];
|
|
1799
|
+
for (let i = 0;i<16;i++) {
|
|
1800
|
+
bytes.push(Math.floor(Math.random() * 16).toString(16));
|
|
1801
|
+
}
|
|
1802
|
+
return bytes.join('');
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
function getRandomChannel()
|
|
1806
|
+
{
|
|
1807
|
+
const channels = [11,15,20,25]
|
|
1808
|
+
return channels[Math.floor(Math.random() * 4)];
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1529
1811
|
if (settings.extPanID === undefined || settings.extPanID == '') {
|
|
1530
1812
|
settings.channel = getRandomChannel();
|
|
1531
1813
|
}
|
|
@@ -1577,13 +1859,20 @@ function load(settings, onChange) {
|
|
|
1577
1859
|
}
|
|
1578
1860
|
}
|
|
1579
1861
|
|
|
1580
|
-
|
|
1581
1862
|
getComPorts(onChange);
|
|
1582
1863
|
|
|
1583
1864
|
//dialog = new MatDialog({EndingTop: '50%'});
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1865
|
+
const keepAliveHandle = startKeepalive();
|
|
1866
|
+
keepAlive(() => {
|
|
1867
|
+
getDevices();
|
|
1868
|
+
getNamedColors();
|
|
1869
|
+
readNVRamBackup(false);
|
|
1870
|
+
sendToWrapper(namespace, 'getGroups', {}, function (data) {
|
|
1871
|
+
groups = data.groups || {};
|
|
1872
|
+
//showGroups();
|
|
1873
|
+
});
|
|
1874
|
+
})
|
|
1875
|
+
|
|
1587
1876
|
//getDebugMessages();
|
|
1588
1877
|
//getMap();
|
|
1589
1878
|
//addCard();
|
|
@@ -1592,13 +1881,10 @@ function load(settings, onChange) {
|
|
|
1592
1881
|
onChange(false);
|
|
1593
1882
|
|
|
1594
1883
|
$('#test-btn').click(function () {
|
|
1595
|
-
console.warn(`isHerdsmanRunning: ${isHerdsmanRunning}`)
|
|
1596
1884
|
if (!isHerdsmanRunning) {
|
|
1597
1885
|
const port = $('#port.value').val();
|
|
1598
|
-
console.warn(`port is ${port}`)
|
|
1599
1886
|
showWaitingDialog(`Trying to connect to ${port}`, 300);
|
|
1600
|
-
|
|
1601
|
-
console.warn(`send to returned with ${JSON.stringify(msg)}`);
|
|
1887
|
+
sendToWrapper(namespace, 'testConnection', { address:port }, function(msg) {
|
|
1602
1888
|
closeWaitingDialog();
|
|
1603
1889
|
if (msg) {
|
|
1604
1890
|
if (msg.error) {
|
|
@@ -1617,7 +1903,6 @@ function load(settings, onChange) {
|
|
|
1617
1903
|
})
|
|
1618
1904
|
// test start commands
|
|
1619
1905
|
$('#show_test_run').click(function () {
|
|
1620
|
-
console.warn(`isHerdsmanRunning: ${isHerdsmanRunning}`)
|
|
1621
1906
|
doTestStart(!isHerdsmanRunning);
|
|
1622
1907
|
});
|
|
1623
1908
|
|
|
@@ -1639,10 +1924,8 @@ function load(settings, onChange) {
|
|
|
1639
1924
|
});
|
|
1640
1925
|
$('#pairing').click(function () {
|
|
1641
1926
|
if (!$('#pairing').hasClass('pulse')) {
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1644
|
-
console.warn('lets pairing');
|
|
1645
|
-
showPairingProcess();
|
|
1927
|
+
openNetwork();
|
|
1928
|
+
} else showPairingProcess();
|
|
1646
1929
|
});
|
|
1647
1930
|
|
|
1648
1931
|
$('#refresh').click(function () {
|
|
@@ -1678,18 +1961,14 @@ function load(settings, onChange) {
|
|
|
1678
1961
|
showChannels();
|
|
1679
1962
|
});
|
|
1680
1963
|
|
|
1681
|
-
sendTo(namespace, 'getGroups', {}, function (data) {
|
|
1682
|
-
groups = data.groups;
|
|
1683
|
-
//showGroups();
|
|
1684
|
-
});
|
|
1685
1964
|
|
|
1686
1965
|
$('#add_group').click(function () {
|
|
1687
|
-
const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
|
|
1966
|
+
const maxind = parseInt(Object.getOwnPropertyNames(groups || {}).reduce((a, b) => a > b ? a : b, 0));
|
|
1688
1967
|
addGroup(maxind + 1, 'Group ' + maxind + 1);
|
|
1689
1968
|
});
|
|
1690
1969
|
|
|
1691
1970
|
$('#add_grp_btn').click(function () {
|
|
1692
|
-
const maxind = parseInt(Object.getOwnPropertyNames(groups).reduce((a, b) => a > b ? a : b, 0));
|
|
1971
|
+
const maxind = parseInt(Object.getOwnPropertyNames(groups || {}).reduce((a, b) => a > b ? a : b, 0));
|
|
1693
1972
|
addGroup(maxind + 1, 'Group ' + maxind + 1);
|
|
1694
1973
|
});
|
|
1695
1974
|
|
|
@@ -1715,6 +1994,7 @@ function load(settings, onChange) {
|
|
|
1715
1994
|
$('.dropdown-trigger').dropdown({constrainWidth: false});
|
|
1716
1995
|
Materialize.updateTextFields();
|
|
1717
1996
|
$('.collapsible').collapsible();
|
|
1997
|
+
|
|
1718
1998
|
Materialize.Tabs.init($('.tabs'));
|
|
1719
1999
|
$('#device-search').keyup(function (event) {
|
|
1720
2000
|
doFilter(event.target.value.toLowerCase());
|
|
@@ -1748,11 +2028,15 @@ function load(settings, onChange) {
|
|
|
1748
2028
|
addExcludeDialog();
|
|
1749
2029
|
});
|
|
1750
2030
|
|
|
2031
|
+
$('#updateData').click(function () {
|
|
2032
|
+
getDevices();
|
|
2033
|
+
});
|
|
2034
|
+
|
|
1751
2035
|
$('#add_binding').click(function () {
|
|
1752
2036
|
addBindingDialog();
|
|
1753
2037
|
});
|
|
1754
2038
|
|
|
1755
|
-
|
|
2039
|
+
sendToWrapper(namespace, 'getLibData', {key: 'cidList'}, function (data) {
|
|
1756
2040
|
cidList = data.list;
|
|
1757
2041
|
});
|
|
1758
2042
|
}
|
|
@@ -1767,13 +2051,22 @@ function showMessages() {
|
|
|
1767
2051
|
$('#stdout_t').text(messages.join('\n'));
|
|
1768
2052
|
}
|
|
1769
2053
|
|
|
1770
|
-
function showPairingProcess() {
|
|
2054
|
+
function showPairingProcess(noextrabuttons) {
|
|
1771
2055
|
if (isHerdsmanRunning) $('#modalpairing').modal({
|
|
1772
2056
|
startingTop: '4%',
|
|
1773
2057
|
endingTop: '10%',
|
|
1774
2058
|
dismissible: false
|
|
1775
2059
|
});
|
|
1776
2060
|
|
|
2061
|
+
if (noextrabuttons) {
|
|
2062
|
+
$('#modalpairing').find('.endpairing').addClass('hide');
|
|
2063
|
+
$('#modalpairing').find('.extendpairing').addClass('hide');
|
|
2064
|
+
}
|
|
2065
|
+
else {
|
|
2066
|
+
$('#modalpairing').find('.endpairing').removeClass('hide');
|
|
2067
|
+
$('#modalpairing').find('.extendpairing').removeClass('hide');
|
|
2068
|
+
}
|
|
2069
|
+
|
|
1777
2070
|
$('#modalpairing').modal('open');
|
|
1778
2071
|
Materialize.updateTextFields();
|
|
1779
2072
|
}
|
|
@@ -1792,24 +2085,25 @@ function doTestStart(start, interactive) {
|
|
|
1792
2085
|
};
|
|
1793
2086
|
// $('#testStartStart').addClass('disabled');
|
|
1794
2087
|
messages = [];
|
|
1795
|
-
if (interactive)
|
|
1796
|
-
|
|
2088
|
+
if (interactive) showPairingProcess(true)
|
|
2089
|
+
|
|
2090
|
+
// showWaitingDialog('Trying to start the zigbee subsystem manually', 120);
|
|
2091
|
+
sendToWrapper(namespace, 'testConnect', { start:true, zigbeeOptions:ovr }, function(msg) {
|
|
1797
2092
|
if (msg) {
|
|
1798
2093
|
closeWaitingDialog();
|
|
1799
|
-
isHerdsmanRunning = msg.status;
|
|
1800
2094
|
updateStartButton(false);
|
|
1801
2095
|
if (msg.status)
|
|
1802
2096
|
$('#testStartStop').removeClass('disabled');
|
|
1803
2097
|
else {
|
|
1804
2098
|
//showMessage(`The zigbee subsystem is not running. Please ensure that the configuration is correct. ${msg.error ? 'Error on start-Attempt ' + msg.error.message : ''}`);
|
|
1805
|
-
$('#
|
|
2099
|
+
$('#testStartStart').removeClass('disabled');
|
|
1806
2100
|
}
|
|
1807
2101
|
}
|
|
1808
2102
|
})
|
|
1809
2103
|
}
|
|
1810
2104
|
else {
|
|
1811
2105
|
//$('#testStartStop').addClass('disabled');
|
|
1812
|
-
|
|
2106
|
+
sendToWrapper(namespace, 'testConnect', { start:false }, function(msg) {
|
|
1813
2107
|
if (msg) {
|
|
1814
2108
|
if (msg.status) $('#testStartStart').removeClass('disabled');
|
|
1815
2109
|
else $('#testStartStop').removeClass('disabled');
|
|
@@ -1847,7 +2141,6 @@ function getDevId(adapterDevId) {
|
|
|
1847
2141
|
|
|
1848
2142
|
|
|
1849
2143
|
function updateStartButton(block) {
|
|
1850
|
-
console.warn(`update start button with${isHerdsmanRunning ? ' Herdsman' : 'out Herdsman'}`);
|
|
1851
2144
|
if (block) {
|
|
1852
2145
|
$('#show_test_run').addClass('disabled');
|
|
1853
2146
|
$('#reset-btn').addClass('disabled');
|
|
@@ -1894,7 +2187,6 @@ socket.emit('subscribeObjects', namespace + '.*');
|
|
|
1894
2187
|
socket.on('stateChange', function (id, state) {
|
|
1895
2188
|
// only watch our own states
|
|
1896
2189
|
if (id.substring(0, namespaceLen) !== namespace) return;
|
|
1897
|
-
//console.log('stateChange', id, state);
|
|
1898
2190
|
if (state) {
|
|
1899
2191
|
if (id.match(/\.info\.pairingMode$/)) {
|
|
1900
2192
|
if (state.val) {
|
|
@@ -1956,14 +2248,12 @@ socket.on('stateChange', function (id, state) {
|
|
|
1956
2248
|
|
|
1957
2249
|
socket.on('objectChange', function (id, obj) {
|
|
1958
2250
|
if (id.substring(0, namespaceLen) !== namespace) return;
|
|
1959
|
-
//console.log('objectChange', id, obj);
|
|
1960
2251
|
if (obj && obj.type == 'device') { // && obj.common.type !== 'group') {
|
|
1961
2252
|
updateDevice(id);
|
|
1962
2253
|
}
|
|
1963
2254
|
if (!obj) {
|
|
1964
2255
|
// delete state or device
|
|
1965
2256
|
const elems = id.split('.');
|
|
1966
|
-
//console.log('elems', elems);
|
|
1967
2257
|
if (elems.length === 3) {
|
|
1968
2258
|
removeDevice(id);
|
|
1969
2259
|
showDevices();
|
|
@@ -2015,10 +2305,11 @@ function showNetworkMap(devices, map) {
|
|
|
2015
2305
|
const createNode = function (dev, mapEntry) {
|
|
2016
2306
|
if (dev.common && (dev.common.type == 'group' || dev.common.deactivated)) return undefined;
|
|
2017
2307
|
const extInfo = (mapEntry && mapEntry.networkAddress) ? `\n (nwkAddr: 0x${mapEntry.networkAddress.toString(16)} | ${mapEntry.networkAddress})` : '';
|
|
2308
|
+
const t = dev._id.replace(namespace + '.', '');
|
|
2018
2309
|
const node = {
|
|
2019
2310
|
id: dev._id,
|
|
2020
2311
|
label: (dev.link_quality > 0 ? dev.common.name : `${dev.common.name}\n(disconnected)`),
|
|
2021
|
-
title:
|
|
2312
|
+
title: `${t} ${extInfo}`,
|
|
2022
2313
|
shape: 'circularImage',
|
|
2023
2314
|
image: dev.common.icon || dev.icon,
|
|
2024
2315
|
imagePadding: {top: 5, bottom: 5, left: 5, right: 5},
|
|
@@ -2027,20 +2318,20 @@ function showNetworkMap(devices, map) {
|
|
|
2027
2318
|
borderWidth: 1,
|
|
2028
2319
|
borderWidthSelected: 4,
|
|
2029
2320
|
};
|
|
2030
|
-
if (dev.
|
|
2321
|
+
if (dev.common && dev.common.type === 'Coordinator') {
|
|
2031
2322
|
// node.shape = 'star';
|
|
2032
2323
|
node.image = 'zigbee.png';
|
|
2033
2324
|
node.label = 'Coordinator';
|
|
2034
2325
|
// delete node.color;
|
|
2035
2326
|
}
|
|
2327
|
+
console.warn(`node for device ${JSON.stringify(node)}`)
|
|
2036
2328
|
return node;
|
|
2037
2329
|
};
|
|
2038
2330
|
|
|
2039
2331
|
if (map.lqis) {
|
|
2040
2332
|
map.lqis.forEach((mapEntry) => {
|
|
2041
|
-
const dev =
|
|
2333
|
+
const dev = getDeviceByIEEE(mapEntry.ieeeAddr);
|
|
2042
2334
|
if (!dev) {
|
|
2043
|
-
//console.log("No dev with ieee "+mapEntry.ieeeAddr);
|
|
2044
2335
|
return;
|
|
2045
2336
|
}
|
|
2046
2337
|
|
|
@@ -2054,7 +2345,7 @@ function showNetworkMap(devices, map) {
|
|
|
2054
2345
|
node = nodes[mapEntry.ieeeAddr];
|
|
2055
2346
|
}
|
|
2056
2347
|
if (node) {
|
|
2057
|
-
const parentDev =
|
|
2348
|
+
const parentDev = getDeviceByIEEE(mapEntry.parent);
|
|
2058
2349
|
const to = parentDev ? parentDev._id : undefined;
|
|
2059
2350
|
const from = dev._id;
|
|
2060
2351
|
let label = mapEntry.lqi.toString();
|
|
@@ -2321,7 +2612,7 @@ function getComPorts(onChange) {
|
|
|
2321
2612
|
// timeout = setTimeout(function () {
|
|
2322
2613
|
// getComPorts(onChange);
|
|
2323
2614
|
// }, 2000);
|
|
2324
|
-
|
|
2615
|
+
sendToWrapper(namespace, 'listUart', null, function (list) {
|
|
2325
2616
|
// if (timeout) {
|
|
2326
2617
|
// clearTimeout(timeout);
|
|
2327
2618
|
// timeout = null;
|
|
@@ -2465,7 +2756,6 @@ function loadDeveloperTab() {
|
|
|
2465
2756
|
const device = devices.find(obj => {
|
|
2466
2757
|
return this.value ===obj.native.id;
|
|
2467
2758
|
});
|
|
2468
|
-
console.warn(`dev selector: ${this.selectedIndex} ${this.value} ->${JSON.stringify(device)}`)
|
|
2469
2759
|
|
|
2470
2760
|
const epList = device ? device.info.endpoints : null;
|
|
2471
2761
|
updateSelect('#ep', epList,
|
|
@@ -2548,7 +2838,7 @@ function loadDeveloperTab() {
|
|
|
2548
2838
|
data = prepareData();
|
|
2549
2839
|
}
|
|
2550
2840
|
sendToZigbee(data.devId, data.ep, data.cid, data.cmd, data.cmdType, data.zclData, data.cfg, function (reply) {
|
|
2551
|
-
console.log('
|
|
2841
|
+
console.log('Send to Zigbee replied with ' + JSON.stringify(reply));
|
|
2552
2842
|
if (reply.hasOwnProperty('localErr')) {
|
|
2553
2843
|
showDevRunInfo(reply.localErr, reply.errMsg, 'yellow');
|
|
2554
2844
|
} else if (reply.hasOwnProperty('localStatus')) {
|
|
@@ -2563,7 +2853,7 @@ function loadDeveloperTab() {
|
|
|
2563
2853
|
|
|
2564
2854
|
responseCodes = null;
|
|
2565
2855
|
// load list of response codes
|
|
2566
|
-
|
|
2856
|
+
sendToWrapper(namespace, 'getLibData', {key: 'respCodes'}, function (data) {
|
|
2567
2857
|
responseCodes = data.list;
|
|
2568
2858
|
});
|
|
2569
2859
|
}
|
|
@@ -2616,7 +2906,7 @@ function sendToZigbee(id, ep, cid, cmd, cmdType, zclData, cfg, callback) {
|
|
|
2616
2906
|
|
|
2617
2907
|
console.log('Send to zigbee, id ' + id + ',ep ' + ep + ', cid ' + cid + ', cmd ' + cmd + ', cmdType ' + cmdType + ', zclData ' + JSON.stringify(zclData));
|
|
2618
2908
|
|
|
2619
|
-
|
|
2909
|
+
sendToWrapper(namespace, 'sendToZigbee', data, function (reply) {
|
|
2620
2910
|
clearTimeout(sendTimeout);
|
|
2621
2911
|
if (callback) {
|
|
2622
2912
|
callback(reply);
|
|
@@ -2664,7 +2954,7 @@ function populateSelector(selectId, key, cid) {
|
|
|
2664
2954
|
updateSelect(selectId, null);
|
|
2665
2955
|
return;
|
|
2666
2956
|
}
|
|
2667
|
-
|
|
2957
|
+
sendToWrapper(namespace, 'getLibData', {key: key, cid: cid}, function (data) {
|
|
2668
2958
|
const list = data.list;
|
|
2669
2959
|
if (key === 'attrIdList') {
|
|
2670
2960
|
updateSelect(selectId, list,
|
|
@@ -2807,7 +3097,7 @@ function deleteGroupConfirmation(id, name) {
|
|
|
2807
3097
|
|
|
2808
3098
|
function updateGroup(newId, newName, remove) {
|
|
2809
3099
|
groups[newId] = newName;
|
|
2810
|
-
|
|
3100
|
+
sendToWrapper(namespace, 'renameGroup', {id: newId, name: newName, remove: remove}, function (msg) {
|
|
2811
3101
|
if (msg && msg.error) {
|
|
2812
3102
|
showMessage(msg.error, _('Error'));
|
|
2813
3103
|
}
|
|
@@ -2817,7 +3107,7 @@ function updateGroup(newId, newName, remove) {
|
|
|
2817
3107
|
|
|
2818
3108
|
function deleteGroup(id) {
|
|
2819
3109
|
delete groups[id];
|
|
2820
|
-
|
|
3110
|
+
sendToWrapper(namespace, 'deleteGroup', id, function (msg) {
|
|
2821
3111
|
if (msg && msg.error) {
|
|
2822
3112
|
showMessage(msg.error, _('Error'));
|
|
2823
3113
|
}
|
|
@@ -2840,7 +3130,7 @@ function updateDev(id, newName, newGroups) {
|
|
|
2840
3130
|
const keys = Object.keys(newGroups);
|
|
2841
3131
|
if (keys && keys.length) {
|
|
2842
3132
|
command.groups = newGroups
|
|
2843
|
-
|
|
3133
|
+
sendToWrapper(namespace, 'updateGroupMembership', command, function (msg) {
|
|
2844
3134
|
closeWaitingDialog();
|
|
2845
3135
|
if (msg && msg.error) {
|
|
2846
3136
|
showMessage(msg.error, _('Error'));
|
|
@@ -2854,7 +3144,7 @@ function updateDev(id, newName, newGroups) {
|
|
|
2854
3144
|
}
|
|
2855
3145
|
else if (needName)
|
|
2856
3146
|
{
|
|
2857
|
-
|
|
3147
|
+
sendToWrapper(namespace, 'renameDevice', command, function(msg) {
|
|
2858
3148
|
//closeWaitingDialog();
|
|
2859
3149
|
if (msg && msg.error) {
|
|
2860
3150
|
showMessage(msg.error, _('Error'));
|
|
@@ -2873,9 +3163,9 @@ function resetConfirmation() {
|
|
|
2873
3163
|
const btn = $('#modalreset .modal-content a.btn');
|
|
2874
3164
|
btn.unbind('click');
|
|
2875
3165
|
btn.click(function (e) {
|
|
2876
|
-
|
|
3166
|
+
sendToWrapper(namespace, 'reset', {mode: e.target.id}, function (err) {
|
|
2877
3167
|
if (err) {
|
|
2878
|
-
console.log(err);
|
|
3168
|
+
console.log(`reset attempt failed with ${err}`);
|
|
2879
3169
|
} else {
|
|
2880
3170
|
console.log('Reset done');
|
|
2881
3171
|
}
|
|
@@ -3104,7 +3394,7 @@ function addBindingDialog() {
|
|
|
3104
3394
|
}
|
|
3105
3395
|
|
|
3106
3396
|
function addBinding(bind_source, bind_source_ep, bind_target, bind_target_ep, unbind_from_coordinator) {
|
|
3107
|
-
|
|
3397
|
+
sendToWrapper(namespace, 'addBinding', {
|
|
3108
3398
|
bind_source: bind_source,
|
|
3109
3399
|
bind_source_ep: bind_source_ep,
|
|
3110
3400
|
bind_target: bind_target,
|
|
@@ -3121,7 +3411,7 @@ function addBinding(bind_source, bind_source_ep, bind_target, bind_target_ep, un
|
|
|
3121
3411
|
}
|
|
3122
3412
|
|
|
3123
3413
|
function editBinding(bind_id, bind_source, bind_source_ep, bind_target, bind_target_ep, unbind_from_coordinator) {
|
|
3124
|
-
|
|
3414
|
+
sendToWrapper(namespace, 'editBinding', {
|
|
3125
3415
|
id: bind_id,
|
|
3126
3416
|
bind_source: bind_source,
|
|
3127
3417
|
bind_source_ep: bind_source_ep,
|
|
@@ -3214,7 +3504,7 @@ function showBinding() {
|
|
|
3214
3504
|
}
|
|
3215
3505
|
|
|
3216
3506
|
function getBinding() {
|
|
3217
|
-
|
|
3507
|
+
sendToWrapper(namespace, 'getBinding', {}, function (msg) {
|
|
3218
3508
|
if (msg) {
|
|
3219
3509
|
if (msg.error) {
|
|
3220
3510
|
showMessage(msg.error, _('Error'));
|
|
@@ -3239,7 +3529,7 @@ function deleteBindingConfirmation(id) {
|
|
|
3239
3529
|
}
|
|
3240
3530
|
|
|
3241
3531
|
function deleteBinding(id) {
|
|
3242
|
-
|
|
3532
|
+
sendToWrapper(namespace, 'delBinding', id, (msg) => {
|
|
3243
3533
|
closeWaitingDialog();
|
|
3244
3534
|
if (msg) {
|
|
3245
3535
|
if (msg.error) {
|
|
@@ -3261,7 +3551,6 @@ function findClName(id) {
|
|
|
3261
3551
|
}
|
|
3262
3552
|
|
|
3263
3553
|
function genDevInfo(device) {
|
|
3264
|
-
//console.log(device);
|
|
3265
3554
|
const dev = (device && device.info) ? device.info.device : undefined;
|
|
3266
3555
|
const mapped = (device && device.info) ? device.info.mapped : undefined;
|
|
3267
3556
|
const endpoints = (device && device.info) ? device.info.endpoints : [];
|
|
@@ -3309,7 +3598,6 @@ function genDevInfo(device) {
|
|
|
3309
3598
|
let epInfo = '';
|
|
3310
3599
|
for (const epind in endpoints) {
|
|
3311
3600
|
const ep = endpoints[epind];
|
|
3312
|
-
console.warn(JSON.stringify(ep));
|
|
3313
3601
|
epInfo +=
|
|
3314
3602
|
`<div style="font-size: 0.9em" class="truncate">
|
|
3315
3603
|
<ul>
|
|
@@ -3342,12 +3630,6 @@ function genDevInfo(device) {
|
|
|
3342
3630
|
return info.join('');
|
|
3343
3631
|
}
|
|
3344
3632
|
|
|
3345
|
-
function showDevInfo(id) {
|
|
3346
|
-
const info = genDevInfo(getDeviceByID(id));
|
|
3347
|
-
$('#devinfo').html(info);
|
|
3348
|
-
$('#modaldevinfo').modal('open');
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
3633
|
let waitingTimeout, waitingInt;
|
|
3352
3634
|
|
|
3353
3635
|
function showWaitingDialog(text, timeout) {
|
|
@@ -3375,7 +3657,7 @@ function closeWaitingDialog() {
|
|
|
3375
3657
|
|
|
3376
3658
|
|
|
3377
3659
|
function showChannels() {
|
|
3378
|
-
|
|
3660
|
+
sendToWrapper(namespace, 'getChannels', {}, function (msg) {
|
|
3379
3661
|
closeWaitingDialog();
|
|
3380
3662
|
if (msg) {
|
|
3381
3663
|
if (msg.error) {
|
|
@@ -3494,21 +3776,20 @@ function addExcludeDialog() {
|
|
|
3494
3776
|
|
|
3495
3777
|
function addExclude(exclude_model) {
|
|
3496
3778
|
if (typeof exclude_model == 'object' && exclude_model.hasOwnProperty('common'))
|
|
3497
|
-
|
|
3779
|
+
sendToWrapper(namespace, 'addExclude', { exclude_model: exclude_model }, function (msg) {
|
|
3498
3780
|
closeWaitingDialog();
|
|
3499
3781
|
if (msg) {
|
|
3500
3782
|
if (msg.error) {
|
|
3501
3783
|
showMessage(msg.error, _('Error'));
|
|
3502
3784
|
}
|
|
3503
3785
|
}
|
|
3504
|
-
console.log('getting excludes ?');
|
|
3505
3786
|
getExclude();
|
|
3506
3787
|
});
|
|
3507
3788
|
else closeWaitingDialog();
|
|
3508
3789
|
}
|
|
3509
3790
|
|
|
3510
3791
|
function getExclude() {
|
|
3511
|
-
|
|
3792
|
+
sendToWrapper(namespace, 'getExclude', {}, function (msg) {
|
|
3512
3793
|
if (msg) {
|
|
3513
3794
|
if (msg.error) {
|
|
3514
3795
|
showMessage(msg.error, _('Error'));
|
|
@@ -3579,14 +3860,13 @@ function deleteExcludeConfirmation(id) {
|
|
|
3579
3860
|
}
|
|
3580
3861
|
|
|
3581
3862
|
function deleteExclude(id) {
|
|
3582
|
-
|
|
3863
|
+
sendToWrapper(namespace, 'delExclude', id, (msg) => {
|
|
3583
3864
|
closeWaitingDialog();
|
|
3584
3865
|
if (msg) {
|
|
3585
3866
|
if (msg.error) {
|
|
3586
3867
|
showMessage(msg.error, _('Error'));
|
|
3587
3868
|
}
|
|
3588
3869
|
}
|
|
3589
|
-
console.log('getting excludes ?');
|
|
3590
3870
|
getExclude();
|
|
3591
3871
|
});
|
|
3592
3872
|
}
|
|
@@ -3669,168 +3949,9 @@ function sortByTitle(element) {
|
|
|
3669
3949
|
return element.querySelector('.card-title').textContent.toLowerCase().trim();
|
|
3670
3950
|
}
|
|
3671
3951
|
|
|
3672
|
-
function getDashCard(dev, groupImage, groupstatus) {
|
|
3673
|
-
const title = dev.common.name,
|
|
3674
|
-
id = dev._id,
|
|
3675
|
-
type = dev.common.type,
|
|
3676
|
-
img_src = (groupImage ? groupImage : dev.common.icon || dev.icon),
|
|
3677
|
-
isActive = !dev.common.deactivated,
|
|
3678
|
-
rooms = [],
|
|
3679
|
-
lang = systemLang || 'en';
|
|
3680
|
-
const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
|
|
3681
|
-
const permitJoinBtn = dev.battery || dev.common.type == 'group' ? '' : `<div class="col tool"><button name="joinCard" class="waves-effect btn-small btn-flat right hoverable green"><i class="material-icons icon-green">leak_add</i></button></div>`;
|
|
3682
|
-
const device_queryBtn = dev.battery || dev.common.type == 'group' ? '' : `<div class="col tool"><button name="deviceQuery" class="waves-effect btn-small btn-flat right hoverable green"><i class="material-icons icon-green">play_for_work</i></button></div>`;
|
|
3683
|
-
const rid = id.split('.').join('_');
|
|
3684
|
-
const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
|
|
3685
|
-
const image = `<img src="${img_src}" width="64px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
|
|
3686
|
-
nwk = (dev.info && dev.info.device) ? dev.info.device.nwk : undefined,
|
|
3687
|
-
battery_cls = getBatteryCls(dev.battery),
|
|
3688
|
-
lqi_cls = getLQICls(dev.link_quality),
|
|
3689
|
-
unconnected_icon = (groupImage ? (groupstatus ? '<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>' : '<div class="col tool"><i class="material-icons icon-red">cancel</i></div>') :'<div class="col tool"><i class="material-icons icon-red">leak_remove</i></div>'),
|
|
3690
|
-
battery = (dev.battery && isActive) ? `<div class="col tool"><i id="${rid}_battery_icon" class="material-icons ${battery_cls}">battery_std</i><div id="${rid}_battery" class="center" style="font-size:0.7em">${dev.battery}</div></div>` : '',
|
|
3691
|
-
lq = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_icon" class="material-icons ${lqi_cls}">network_check</i><div id="${rid}_link_quality" class="center" style="font-size:0.7em">${dev.link_quality}</div></div>` : (isActive ? unconnected_icon : ''),
|
|
3692
|
-
//status = (dev.link_quality > 0 && isActive) ? `<div class="col tool"><i class="material-icons icon-green">check_circle</i></div>` : (groupImage || !isActive ? '' : `<div class="col tool"><i class="material-icons icon-black">leak_remove</i></div>`),
|
|
3693
|
-
//infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '',
|
|
3694
|
-
idleTime = (dev.link_quality_lc > 0 && isActive) ? `<div class="col tool"><i id="${rid}_link_quality_lc_icon" class="material-icons idletime">access_time</i><div id="${rid}_link_quality_lc" class="center" style="font-size:0.7em">${getIdleTime(dev.link_quality_lc)}</div></div>` : '';
|
|
3695
|
-
const info = (dev.statesDef) ? dev.statesDef.map((stateDef) => {
|
|
3696
|
-
const id = stateDef.id;
|
|
3697
|
-
const sid = id.split('.').join('_');
|
|
3698
|
-
let val = stateDef.val || '';
|
|
3699
|
-
if (stateDef.role === 'switch' && stateDef.write) {
|
|
3700
|
-
val = `<span class="switch"><label><input type="checkbox" ${(val) ? 'checked' : ''}><span class="lever"></span></label></span>`;
|
|
3701
|
-
} else if (stateDef.role === 'level.dimmer' && stateDef.write) {
|
|
3702
|
-
val = `<span class="range-field dash"><input type="range" min="0" max="100" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
|
|
3703
|
-
} else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
|
|
3704
|
-
val = `<span class="range-field dash"><input type="range" min="150" max="500" ${(val != undefined) ? `value="${val}"` : ''} /></span>`;
|
|
3705
|
-
} else if (stateDef.type === 'boolean') {
|
|
3706
|
-
const disabled = (stateDef.write) ? '' : 'disabled="disabled"';
|
|
3707
|
-
val = `<label class="dash"><input type="checkbox" ${(val == true) ? 'checked=\'checked\'' : ''} ${disabled}/><span></span></label>`;
|
|
3708
|
-
} else if (stateDef.role === 'level.color.rgb') {
|
|
3709
|
-
const options = []
|
|
3710
|
-
for (const key of namedColors) {
|
|
3711
|
-
options.push(`<option value="${key}" ${val===key ? 'selected' : ''}>${key}</option>`);
|
|
3712
|
-
}
|
|
3713
|
-
val = `<select class="browser-default enum" style="color : white; background-color: grey; height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
|
|
3714
|
-
} else if (stateDef.states && stateDef.write) {
|
|
3715
|
-
let options;
|
|
3716
|
-
if (typeof stateDef.states == 'string') {
|
|
3717
|
-
const sts = stateDef.states.split(';');
|
|
3718
|
-
if (sts.length < 2) return '';
|
|
3719
|
-
options = sts.map((item) => {
|
|
3720
|
-
const v = item.split(':');
|
|
3721
|
-
return `<option value="${v[0]}" ${(val == v[0]) ? 'selected' : ''}>${v[1]}</option>`;
|
|
3722
|
-
});
|
|
3723
|
-
} else {
|
|
3724
|
-
options = [];
|
|
3725
|
-
for (const [key, value] of Object.entries(stateDef.states)) {
|
|
3726
|
-
options.push(`<option value="${key}" ${(val == key) ? 'selected' : ''}>${key}</option>`);
|
|
3727
|
-
}
|
|
3728
|
-
}
|
|
3729
|
-
if (options.length < 2) return '';
|
|
3730
|
-
val = `<select class="browser-default enum" style="color : white; background-color: grey; height: 16px; padding: 0; width: auto; display: inline-block">${options.join('')}</select>`;
|
|
3731
|
-
} else if (stateDef.write) {
|
|
3732
|
-
return;
|
|
3733
|
-
// val = `<span class="input-field dash value"><input class="dash value" id="${stateDef.name}" value="${val}"></input></span>`;
|
|
3734
|
-
}
|
|
3735
|
-
else {
|
|
3736
|
-
val = `<span class="dash value">${val ? val : '(null)'} ${(stateDef.unit) ? stateDef.unit : ''}</span>`;
|
|
3737
|
-
}
|
|
3738
|
-
return `<li><span class="label dash truncate">${stateDef.name}</span><span id=${sid} oid=${id} class="state">${val}</span></li>`;
|
|
3739
|
-
}).join('') : '';
|
|
3740
|
-
const dashCard = `
|
|
3741
|
-
<div class="card-content zcard ${isActive ? '' : 'bg_red'}">
|
|
3742
|
-
<div style="cursor: pointer">
|
|
3743
|
-
<span class="top right small" style="border-radius: 50%">
|
|
3744
|
-
${device_queryBtn}
|
|
3745
|
-
${permitJoinBtn}
|
|
3746
|
-
</span>
|
|
3747
|
-
<div class="flip">
|
|
3748
|
-
<span class="top right small" style="border-radius: 50%">
|
|
3749
|
-
${idleTime}
|
|
3750
|
-
${battery}
|
|
3751
|
-
${lq}
|
|
3752
|
-
</span>
|
|
3753
|
-
<span class="card-title truncate">${title}</span>
|
|
3754
|
-
</div>
|
|
3755
|
-
</div>
|
|
3756
|
-
<i class="left">${image}</i>
|
|
3757
|
-
<div style="min-height:88px; font-size: 0.8em; height: 130px; overflow-y: auto" class="truncate">
|
|
3758
|
-
<ul>
|
|
3759
|
-
${(isActive ? info : 'Device deactivated')}
|
|
3760
|
-
</ul>
|
|
3761
|
-
</div>
|
|
3762
|
-
<div class="footer right-align"></div>
|
|
3763
|
-
</div>`;
|
|
3764
|
-
|
|
3765
|
-
return dashCard;
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
function setDashStates(id, state) {
|
|
3769
|
-
const devId = getDevId(id);
|
|
3770
|
-
const dev = getDeviceByID(devId);
|
|
3771
|
-
if (dev) {
|
|
3772
|
-
const stateDef = dev.statesDef.find((stateDef) => stateDef.id == id);
|
|
3773
|
-
if (stateDef) {
|
|
3774
|
-
const sid = id.split('.').join('_');
|
|
3775
|
-
if (stateDef.role === 'switch' && stateDef.write) {
|
|
3776
|
-
$(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
|
|
3777
|
-
} else if (stateDef.role === 'level.dimmer' && stateDef.write) {
|
|
3778
|
-
$(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
|
|
3779
|
-
} else if (stateDef.role === 'level.color.temperature' && stateDef.write) {
|
|
3780
|
-
$(`#${sid}`).find('input[type=\'range\']').prop('value', state.val);
|
|
3781
|
-
} else if (stateDef.states && stateDef.write) {
|
|
3782
|
-
$(`#${sid}`).find(`select option[value=${state.val}]`).prop('selected', true);
|
|
3783
|
-
} else if (stateDef.type === 'boolean') {
|
|
3784
|
-
$(`#${sid}`).find('input[type=\'checkbox\']').prop('checked', state.val);
|
|
3785
|
-
} else {
|
|
3786
|
-
$(`#${sid}`).find('.value').text(`${state.val} ${(stateDef.unit) ? stateDef.unit : ''}`);
|
|
3787
|
-
}
|
|
3788
|
-
}
|
|
3789
|
-
}
|
|
3790
|
-
}
|
|
3791
|
-
|
|
3792
|
-
function hookControls() {
|
|
3793
|
-
$('input[type=\'checkbox\']').change(function (event) {
|
|
3794
|
-
const val = $(this).is(':checked');
|
|
3795
|
-
const id = $(this).parents('.state').attr('oid');
|
|
3796
|
-
sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
|
|
3797
|
-
//console.log(data);
|
|
3798
|
-
});
|
|
3799
|
-
});
|
|
3800
|
-
$('input[type=\'range\']').change(function (event) {
|
|
3801
|
-
const val = $(this).val();
|
|
3802
|
-
const id = $(this).parents('.state').attr('oid');
|
|
3803
|
-
sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
|
|
3804
|
-
//console.log(data);
|
|
3805
|
-
});
|
|
3806
|
-
});
|
|
3807
|
-
$('.state select').on('change', function () {
|
|
3808
|
-
const val = $(this).val();
|
|
3809
|
-
const id = $(this).parents('.state').attr('oid');
|
|
3810
|
-
sendTo(namespace, 'setState', {id: id, val: val}, function (data) {
|
|
3811
|
-
//console.log(data);
|
|
3812
|
-
});
|
|
3813
|
-
});
|
|
3814
|
-
}
|
|
3815
|
-
|
|
3816
|
-
function getIdleTime(value) {
|
|
3817
|
-
return (value) ? moment(new Date(value)).fromNow(true) : '';
|
|
3818
|
-
}
|
|
3819
|
-
|
|
3820
|
-
function updateCardTimer() {
|
|
3821
|
-
if (devices) {
|
|
3822
|
-
devices.forEach((dev) => {
|
|
3823
|
-
const id = dev._id;
|
|
3824
|
-
if (id) {
|
|
3825
|
-
const rid = id.split('.').join('_');
|
|
3826
|
-
$(`#${rid}_link_quality_lc`).text(getIdleTime(dev.link_quality_lc));
|
|
3827
|
-
}
|
|
3828
|
-
});
|
|
3829
|
-
}
|
|
3830
|
-
}
|
|
3831
3952
|
|
|
3832
3953
|
function updateDevice(id) {
|
|
3833
|
-
|
|
3954
|
+
sendToWrapper(namespace, 'getDevice', {id: id}, function (msg) {
|
|
3834
3955
|
if (msg) {
|
|
3835
3956
|
const devs = msg.devices;
|
|
3836
3957
|
if (devs) {
|
|
@@ -3860,13 +3981,13 @@ function swapActive(id) {
|
|
|
3860
3981
|
const dev = getDeviceByID(id);
|
|
3861
3982
|
if (dev && dev.common) {
|
|
3862
3983
|
dev.common.deactivated = !(dev.common.deactivated);
|
|
3863
|
-
|
|
3984
|
+
sendToWrapper(namespace, 'setDeviceActivated', {id: id, deactivated: dev.common.deactivated}, function () {
|
|
3864
3985
|
showDevices();
|
|
3865
3986
|
});
|
|
3866
3987
|
}
|
|
3867
3988
|
}
|
|
3868
3989
|
|
|
3869
|
-
function
|
|
3990
|
+
function reconfigureConfirmation(id) {
|
|
3870
3991
|
const text = translateWord(`Do you really want to reconfigure device?`);
|
|
3871
3992
|
$('#modalreconfigure').find('p').text(text);
|
|
3872
3993
|
$('#modalreconfigure a.btn[name=\'yes\']').unbind('click');
|
|
@@ -3878,7 +3999,7 @@ function reconfigureDlg(id) {
|
|
|
3878
3999
|
}
|
|
3879
4000
|
|
|
3880
4001
|
function reconfigureDevice(id) {
|
|
3881
|
-
|
|
4002
|
+
sendToWrapper(namespace, 'reconfigure', {id: id}, function (msg) {
|
|
3882
4003
|
closeWaitingDialog();
|
|
3883
4004
|
if (msg) {
|
|
3884
4005
|
if (msg.error) {
|
|
@@ -3899,34 +4020,28 @@ function validateConfigData(key, val) {
|
|
|
3899
4020
|
if (validatableKeys.indexOf(key) < 0 || !val) return;
|
|
3900
4021
|
if (warnLevel[key]) {
|
|
3901
4022
|
if (warnLevel[key](val)) {
|
|
3902
|
-
//console.warn(`warning set for ${key} (${val})`)
|
|
3903
4023
|
$(`#${key}_ALERT`).removeClass('hide')
|
|
3904
4024
|
} else $(`#${key}_ALERT`).addClass('hide')
|
|
3905
4025
|
}
|
|
3906
4026
|
if (nvRamBackup[key]) {
|
|
3907
|
-
//console.warn(`value of ${key} is ${val} (${nvRamBackup[key]})`);
|
|
3908
4027
|
if ((typeof val == 'string' && typeof nvRamBackup[key] == 'string' && val.toLowerCase == nvRamBackup[key].toLowerCase) || val == nvRamBackup[key])
|
|
3909
4028
|
{
|
|
3910
|
-
//console.warn(`ok set for ${key} (${val})`)
|
|
3911
4029
|
$(`#${key}_OK`).removeClass('hide')
|
|
3912
4030
|
$(`#${key}_NOK`).addClass('hide')
|
|
3913
4031
|
}
|
|
3914
4032
|
else
|
|
3915
4033
|
{
|
|
3916
|
-
//console.warn(`nok set for ${key} (${val})`)
|
|
3917
4034
|
$(`#${key}_OK`).addClass('hide')
|
|
3918
4035
|
$(`#${key}_NOK`).removeClass('hide')
|
|
3919
4036
|
}
|
|
3920
4037
|
}
|
|
3921
4038
|
else {
|
|
3922
|
-
//console.warn(`noval set for ${key} (${val})`)
|
|
3923
4039
|
$(`#${key}_OK`).addClass('hide')
|
|
3924
4040
|
$(`#${key}_NOK`).addClass('hide')
|
|
3925
4041
|
}
|
|
3926
4042
|
}
|
|
3927
4043
|
|
|
3928
4044
|
function validateNVRamBackup(update, src) {
|
|
3929
|
-
//console.warn('validateNVRam');
|
|
3930
4045
|
const validatedKeys = src ? [src] : validatableKeys;
|
|
3931
4046
|
const validator = {};
|
|
3932
4047
|
for (const key of validatedKeys) {
|
|
@@ -3944,9 +4059,7 @@ function validateNVRamBackup(update, src) {
|
|
|
3944
4059
|
|
|
3945
4060
|
|
|
3946
4061
|
function readNVRamBackup(update) {
|
|
3947
|
-
|
|
3948
|
-
sendTo(namespace, 'readNVRam', {}, function(msg) {
|
|
3949
|
-
console.warn(JSON.stringify(msg));
|
|
4062
|
+
sendToWrapper(namespace, 'readNVRam', {}, function(msg) {
|
|
3950
4063
|
if (msg) {
|
|
3951
4064
|
if (msg.error && update) {
|
|
3952
4065
|
if (msg.error.includes('ENOENT')) showMessage('Unable to read nvRam backup - no backup available.',_('Error'))
|