iobroker.zigbee 3.1.6 → 3.2.1
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 +33 -15
- package/admin/admin.js +511 -149
- package/admin/img/group.png +0 -0
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/img/restore_backup.png +0 -0
- package/admin/index_m.html +147 -9
- package/admin/tab_m.html +7 -8
- package/docs/de/img/edit_grp.png +0 -0
- package/docs/de/img/edit_image.png +0 -0
- package/docs/de/readme.md +2 -2
- package/docs/en/readme.md +2 -2
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +27 -27
- package/lib/DeviceDebug.js +1 -1
- package/lib/backup.js +55 -26
- package/lib/commands.js +99 -60
- package/lib/developer.js +0 -0
- package/lib/devices.js +10 -1
- package/lib/exclude.js +2 -1
- package/lib/exposes.js +17 -2
- package/lib/groups.js +2 -2
- package/lib/localConfig.js +48 -18
- package/lib/ota.js +0 -0
- package/lib/statescontroller.js +81 -50
- package/lib/utils.js +41 -0
- package/lib/zbDelayedAction.js +1 -1
- package/lib/zbDeviceAvailability.js +3 -3
- package/lib/zbDeviceConfigure.js +0 -0
- package/lib/zbDeviceEvent.js +4 -2
- package/lib/zigbeecontroller.js +40 -20
- package/main.js +56 -37
- package/package.json +1 -3
package/admin/admin.js
CHANGED
|
@@ -94,12 +94,12 @@ function keepAlive(callback) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function startKeepalive() {
|
|
97
|
-
return setInterval(
|
|
97
|
+
return setInterval(() => UpdateAdapterAlive(false), 120000);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
function UpdateAdapterAlive(state) {
|
|
101
|
-
if (connectionStatus.connected === state) return;
|
|
102
101
|
connectionStatus.time = Date.now();
|
|
102
|
+
if (connectionStatus.connected === state) return;
|
|
103
103
|
if (state) {
|
|
104
104
|
$('#adapterStopped_btn').addClass('hide');
|
|
105
105
|
$('#code_pairing').removeClass('disabled');
|
|
@@ -185,7 +185,7 @@ function sanitizeModelParameter(parameter) {
|
|
|
185
185
|
try {
|
|
186
186
|
return parameter.replace(replaceByUnderscore, '_');
|
|
187
187
|
}
|
|
188
|
-
catch {}
|
|
188
|
+
catch { /* intentionally empty*/ }
|
|
189
189
|
return parameter;
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -196,29 +196,41 @@ function sanitizeModelParameter(parameter) {
|
|
|
196
196
|
////
|
|
197
197
|
|
|
198
198
|
const LocalDataDisplayValues = {
|
|
199
|
-
unfoldedModels :
|
|
200
|
-
unfoldedDevices :
|
|
199
|
+
unfoldedModels : [], // [{ model:plug01, devices: true/false, options: true/false}]
|
|
200
|
+
unfoldedDevices : [], // [{ device:0xdeadbeefdeadbeef, show: true/false}]
|
|
201
201
|
buttonSet: new Set(),
|
|
202
202
|
showModels: true,
|
|
203
|
+
sortedKeys : [],
|
|
204
|
+
sortMethod: function (a, b) { return 0 },
|
|
205
|
+
filterMethod: function (a) { return true },
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function updateFoldModel(model, devices, options) {
|
|
209
|
+
const m = LocalDataDisplayValues.unfoldedModels.find((c) => c.model === model);
|
|
210
|
+
if (!m) {
|
|
211
|
+
const ml = {model, devices: (devices === undefined ? false: devices), options: (options === undefined ? false : options)}
|
|
212
|
+
LocalDataDisplayValues.unfoldedModels.push(ml)
|
|
213
|
+
return ml;;
|
|
214
|
+
}
|
|
215
|
+
if (devices) m.devices = !m.devices;
|
|
216
|
+
if (options) m.options = !m.options;
|
|
217
|
+
return m;
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
|
|
206
|
-
function getModelData(data, models) {
|
|
221
|
+
function getModelData(data, models, keys) {
|
|
207
222
|
const Html = [];
|
|
208
|
-
// Html.push(`<ul class="collapsible">`);
|
|
209
223
|
const s = new Set();
|
|
210
|
-
for (const k of
|
|
224
|
+
for (const k of keys) {
|
|
211
225
|
const model = models[k];
|
|
212
226
|
const key = model.model.model;
|
|
213
|
-
console.warn(`getmodeldata: model is ${key}, sO: ${JSON.stringify(model.setOptions)}`);
|
|
214
|
-
const numOptions = Object.keys(model.setOptions).length;
|
|
215
|
-
const foldData =
|
|
227
|
+
//console.warn(`getmodeldata: model is ${key}, sO: ${JSON.stringify(model.setOptions)}`);
|
|
228
|
+
const numOptions = Object.keys(model.setOptions).length + ((typeof model.setOptions.options === 'object' && model.setOptions.options != null) ? Object.keys(model.setOptions.options).length-1 : 0);
|
|
229
|
+
const foldData = updateFoldModel(key, undefined, undefined);
|
|
216
230
|
let numrows = 1;
|
|
217
231
|
if (foldData.devices) numrows += model.devices.length;
|
|
218
232
|
if (numOptions > 0) numrows += 1;
|
|
219
233
|
if (foldData.options) numrows += numOptions;
|
|
220
|
-
//const numrows = (foldData.devices ? model.devices.length : 0) + (foldData.options ? numOptions : 0) + numOptions > 0 ? 2 : 1;
|
|
221
|
-
console.warn(`numrows is ${numrows} with ${model.devices.length} ${foldData.devices ? 'shown' : 'hidden'} devices and ${numOptions} options ${foldData.options ? 'shown' : 'hidden'}`);
|
|
222
234
|
const d_btn_name = `d_toggle_${k}`;
|
|
223
235
|
const e_btn_name = `m_edit_${k}`;
|
|
224
236
|
const d_btn_tip = `fold / unfold devices of ${key}`;
|
|
@@ -227,25 +239,61 @@ function getModelData(data, models) {
|
|
|
227
239
|
const e_btn = btnParam(e_btn_name, e_btn_tip, 'edit', 'green', false)
|
|
228
240
|
LocalDataDisplayValues.buttonSet.add(d_btn_name);
|
|
229
241
|
LocalDataDisplayValues.buttonSet.add(e_btn_name);
|
|
230
|
-
|
|
242
|
+
const devtxt = (model.devices.length && !foldData.devices) ? `${model.devices.length} ${model.model.type}${model.devices.length > 1 ? 's' : ''}` : '';
|
|
243
|
+
Html.push(`<tr id="datarowodd">
|
|
244
|
+
<td rowspan="${numrows}" width="15%"><img src=${model.model.icon} class="dev_list"></td>
|
|
245
|
+
<td colspan="2">Model ${key}</td><td>${devtxt}</td>
|
|
246
|
+
<td>${d_btn} ${e_btn}</td></tr>`)
|
|
231
247
|
let cnt = 0;
|
|
232
248
|
if (foldData.devices) {
|
|
249
|
+
let isOdd = false;
|
|
233
250
|
for (const dev of model.devices) {
|
|
234
251
|
let devieee = dev._id.replace(`${namespace}.`, '');
|
|
235
252
|
|
|
236
253
|
if (devieee == undefined) devieee = 'unknown' + cnt++;
|
|
237
|
-
|
|
238
|
-
|
|
254
|
+
//LocalDataDisplayValues.buttonSet.add(`d_delete_${devieee}`);
|
|
255
|
+
LocalDataDisplayValues.buttonSet.add(`d_delall_${k}-${devieee}`);
|
|
256
|
+
LocalDataDisplayValues.buttonSet.add(`d_disen_${k}-${devieee}`);
|
|
257
|
+
|
|
258
|
+
//const bn = btnParam(`d_delete_${devieee}`, `delete device ${devieee}`, 'delete', 'red darken-4', false);
|
|
259
|
+
const bna = btnParam(`d_delall_${k}-${devieee}`, `completely delete device ${devieee}`, 'delete_forever', 'red accent-4', false);
|
|
260
|
+
const bta = !dev.common.deactivated ? btnParam(`d_disen_${k}-${devieee}`, `disable device ${devieee}`, 'power_settings_new', 'green accent-4', false) : btnParam(`d_disen_${k}-${devieee}`, `enable device ${devieee}`, 'power_settings_new', 'red accent-4', false);
|
|
261
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}${dev.common.deactivated ? '_red' : ''}"><td width="1%"><i class="material-icons small">devices</i></td><td width="25%">${devieee}</td><td width="45%">${dev.common.name}</td><td width="10%">${bna}${bta}<td></tr>`)
|
|
262
|
+
isOdd = !isOdd;
|
|
239
263
|
}
|
|
240
264
|
}
|
|
241
265
|
if (numOptions > 0) {
|
|
242
266
|
const o_btn_name = `o_toggle_${k}`;
|
|
243
267
|
const o_btn_tip = `fold / unfold options for Model ${key}`;
|
|
244
268
|
LocalDataDisplayValues.buttonSet.add(o_btn_name);
|
|
245
|
-
|
|
269
|
+
const opttxt = (numOptions > 0 && !(foldData.options)) ? `${numOptions} global option${numOptions > 1 ? 's' : ''}` :''
|
|
270
|
+
Html.push(`<tr id="datarowodd">
|
|
271
|
+
<td colspan="2">Model ${key}</td><td>${opttxt}</td>
|
|
272
|
+
<td>${btnParam(o_btn_name, o_btn_tip, foldData.options ? 'expand_less' : 'expand_more')}</td></tr>`)
|
|
246
273
|
if (foldData.options) {
|
|
274
|
+
let isOdd = false;
|
|
247
275
|
for (const key of Object.keys(model.setOptions)) {
|
|
248
|
-
|
|
276
|
+
if (typeof model.setOptions[key] === 'object') {
|
|
277
|
+
const oo = model.setOptions[key];
|
|
278
|
+
for (const ok of Object.keys(oo)) {
|
|
279
|
+
LocalDataDisplayValues.buttonSet.add(`o_delete_${k}-${ok}`);
|
|
280
|
+
const btn = btnParam(`o_delete_${k}-${ok}`, `delete option ${ok}`, 'delete', 'red darken-4', false);
|
|
281
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td width="25%">${ok}</td><td width="45%" ${oo[ok] === undefined ? 'id="datared">"not set on model"' : '>'+oo[ok]}</td><td>${btn}</td></tr>`)
|
|
282
|
+
isOdd = !isOdd;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
LocalDataDisplayValues.buttonSet.add(`l_delete_${k}-${key}`);
|
|
287
|
+
const btn = btnParam(`l_delete_${k}-${key}`, `delete option ${key}`, 'delete', 'red darken-4', false);
|
|
288
|
+
if (key==='icon') {
|
|
289
|
+
const icontext = model.setOptions[key] === undefined ? 'id="datared">"not set on model"' : `>${model.setOptions[key]}`;
|
|
290
|
+
const icon = model.setOptions[key]=== undefined ? '' : `<img src=${model.setOptions[key]} height="32px" class="sml_list">`;
|
|
291
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td width="25%">${key}</td><td valign="middle" width="45%" ${icontext}</td><td>${btn}${icon}</td></tr>`)
|
|
292
|
+
}
|
|
293
|
+
else
|
|
294
|
+
Html.push(`<tr id="datarow${isOdd ? 'opt':'even'}"><td width="1%"><i class="material-icons small">blur_circular</i></td><td width="25%">${key}</td><td width="45%" ${model.setOptions[key] === undefined ? 'id="datared">"not set on model"' : '>'+model.setOptions[key]}</td><td>${btn}</td></tr>`)
|
|
295
|
+
isOdd = !isOdd;
|
|
296
|
+
}
|
|
249
297
|
}
|
|
250
298
|
}
|
|
251
299
|
}
|
|
@@ -261,7 +309,8 @@ function btnParam(id, tooltip, icon, color, disabled) {
|
|
|
261
309
|
|
|
262
310
|
function getDeviceData(deviceList, withIcon) {
|
|
263
311
|
const Html = [];
|
|
264
|
-
|
|
312
|
+
return Html;
|
|
313
|
+
/*for (const dev of deviceList) {
|
|
265
314
|
const rowspan = dev.options ? Object.keys(dev.options).length + 2 : 2;
|
|
266
315
|
const iconLink = `<img src=${dev.common.icon} class="dev_list">`;
|
|
267
316
|
const devieee = dev._id.replace(`${namespace}.`, '');
|
|
@@ -282,58 +331,81 @@ function getDeviceData(deviceList, withIcon) {
|
|
|
282
331
|
}
|
|
283
332
|
}
|
|
284
333
|
}
|
|
285
|
-
return Html
|
|
334
|
+
return Html;*/
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function sortAndFilter(filter, sort) {
|
|
338
|
+
const fFun = filter || LocalDataDisplayValues.filterMethod;
|
|
339
|
+
console.warn('once:='+JSON.stringify(models['m_0'].setOptions))
|
|
340
|
+
console.warn('twice:='+ JSON.stringify(models['m_1'].setOptions))
|
|
341
|
+
let filterMap = LocalDataDisplayValues.sortedKeys = Object.keys(models);
|
|
342
|
+
if (LocalDataDisplayValues.searchVal && LocalDataDisplayValues.searchVal.length) {
|
|
343
|
+
filterMap = filterMap.filter((a) => {
|
|
344
|
+
return models[a]?.model?.model?.toLowerCase().includes(LocalDataDisplayValues.searchVal)
|
|
345
|
+
});
|
|
346
|
+
console.warn(`${JSON.stringify(LocalDataDisplayValues.searchVal)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
347
|
+
}
|
|
348
|
+
if (typeof fFun == 'function') {
|
|
349
|
+
console.warn(`${JSON.stringify(filterMap)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
350
|
+
filterMap = filterMap.filter(fFun);
|
|
351
|
+
}
|
|
352
|
+
console.warn(JSON.stringify(filterMap));
|
|
353
|
+
const sFun = sort || LocalDataDisplayValues.sortMethod;
|
|
354
|
+
if (typeof sFun == 'function') {
|
|
355
|
+
console.warn(`${JSON.stringify(filterMap)} - ${JSON.stringify(models['m_1'].model)}`);
|
|
356
|
+
filterMap = filterMap.sort(sFun);
|
|
357
|
+
}
|
|
358
|
+
console.warn(JSON.stringify(filterMap));
|
|
359
|
+
if (typeof filter == 'function') LocalDataDisplayValues.filterMethod = filter;
|
|
360
|
+
if (typeof sort == 'function') LocalDataDisplayValues.sortMethod = sort;
|
|
361
|
+
return filterMap;
|
|
286
362
|
}
|
|
287
363
|
|
|
288
364
|
function showLocalData() {
|
|
289
365
|
LocalDataDisplayValues.buttonSet.clear();
|
|
290
|
-
|
|
366
|
+
;
|
|
367
|
+
const ModelHtml = getModelData(devices, models, sortAndFilter(undefined, undefined));
|
|
291
368
|
const DeviceHtml = getDeviceData(devices);
|
|
292
369
|
const sm = LocalDataDisplayValues.showModels;
|
|
293
|
-
const dmtoggle = btnParam('t_all_models',
|
|
370
|
+
//const dmtoggle = btnParam('t_all_models', 'Refresh models', 'developer_board');
|
|
294
371
|
|
|
295
372
|
const RowSpan = sm ? ModelHtml.length +2 : DeviceHtml.length + 2;
|
|
296
373
|
const Html = [];
|
|
374
|
+
|
|
297
375
|
if (sm) {
|
|
298
|
-
Html.push(`<table style="width:100%"><tr id="datatable"><th rowspan="${RowSpan}"> </th><th colspan=
|
|
376
|
+
Html.push(`<table style="width:100%"><tr id="datatable"><th rowspan="${RowSpan}"> </th><th colspan=4></th><th></th><th rowspan="${RowSpan}"> </th></tr>`);
|
|
299
377
|
Html.push(ModelHtml.join(''));
|
|
300
378
|
}
|
|
301
|
-
else {
|
|
302
|
-
Html.push(`<table style="width:100%"><tr id="datatable"><th rowspan="${RowSpan}"> </th><th colspan=
|
|
379
|
+
/*else {
|
|
380
|
+
Html.push(`<table style="width:100%"><tr id="datatable"><th rowspan="${RowSpan}"> </th><th colspan=4>Device Data</th><th>${dmtoggle}</th><th rowspan="${RowSpan}"> </th></tr>`)
|
|
303
381
|
Html.push(DeviceHtml.join(''));
|
|
304
|
-
}
|
|
305
|
-
Html.push(`<tr id="datatable"><td colspan="
|
|
382
|
+
}*/
|
|
383
|
+
Html.push(`<tr id="datatable"><td colspan="5"></td></tr>`)
|
|
306
384
|
Html.push('</table>');
|
|
307
|
-
|
|
385
|
+
//Html.push('</div></div>');
|
|
386
|
+
$('#tab-overrides-content').html(Html.join(''));
|
|
308
387
|
|
|
309
|
-
|
|
310
|
-
LocalDataDisplayValues.showModels = !LocalDataDisplayValues.showModels;
|
|
311
|
-
|
|
312
|
-
})
|
|
388
|
+
/*$('#t_all_models').click(function () {
|
|
389
|
+
//LocalDataDisplayValues.showModels = !LocalDataDisplayValues.showModels;
|
|
390
|
+
getDevices();
|
|
391
|
+
});*/
|
|
313
392
|
|
|
314
|
-
console.warn(`lddv is ${JSON.stringify(LocalDataDisplayValues)}`)
|
|
393
|
+
//console.warn(`lddv is ${JSON.stringify(LocalDataDisplayValues)}`)
|
|
315
394
|
for (const item of LocalDataDisplayValues.buttonSet) {
|
|
316
|
-
console.warn(`adding click to ${item}`)
|
|
317
395
|
if (item.startsWith('d_toggle_')) $(`#${item}`).click(function () {
|
|
318
|
-
const key = item.
|
|
319
|
-
console.warn(`clicked ${item}`);
|
|
320
|
-
|
|
321
|
-
LocalDataDisplayValues.unfoldedModels[key].devices =! LocalDataDisplayValues.unfoldedModels[key].devices;
|
|
322
|
-
else
|
|
323
|
-
LocalDataDisplayValues.unfoldedModels[key] = { devices:true, options: false };
|
|
396
|
+
const key = item.replace('d_toggle_', '');
|
|
397
|
+
//console.warn(`clicked ${item}`);
|
|
398
|
+
updateFoldModel(models[key].model.model, true, false)
|
|
324
399
|
showLocalData();
|
|
325
400
|
});
|
|
326
401
|
if (item.startsWith('o_toggle_')) $(`#${item}`).click(function () {
|
|
327
|
-
console.warn(`clicked ${item}`);
|
|
402
|
+
//console.warn(`clicked ${item}`);
|
|
328
403
|
const key = item.substring(9);
|
|
329
|
-
|
|
330
|
-
LocalDataDisplayValues.unfoldedModels[key].options = !LocalDataDisplayValues.unfoldedModels[key].options;
|
|
331
|
-
else
|
|
332
|
-
LocalDataDisplayValues.unfoldedModels[key] = { devices:false, options: true };
|
|
404
|
+
updateFoldModel(models[key].model.model, false, true)
|
|
333
405
|
showLocalData();
|
|
334
406
|
})
|
|
335
407
|
if (item.startsWith('do_toggle_')) $(`#${item}`).click(function () {
|
|
336
|
-
console.warn(`clicked ${item}`);
|
|
408
|
+
//console.warn(`clicked ${item}`);
|
|
337
409
|
const key = item.substring(10);
|
|
338
410
|
if (LocalDataDisplayValues.unfoldedDevices.hasOwnProperty(key))
|
|
339
411
|
LocalDataDisplayValues.unfoldedDevices[key] =! LocalDataDisplayValues.unfoldedDevices[key];
|
|
@@ -341,10 +413,63 @@ function showLocalData() {
|
|
|
341
413
|
LocalDataDisplayValues.unfoldedDevices[key] = true;
|
|
342
414
|
showLocalData();
|
|
343
415
|
})
|
|
344
|
-
|
|
345
|
-
|
|
416
|
+
if (item.startsWith('m_edit_')) $(`#${item}`).click(function () {
|
|
417
|
+
//console.warn(`clicked ${item}`);
|
|
418
|
+
const key = item.substring(7);
|
|
419
|
+
editDeviceOptions(models[key], true);
|
|
420
|
+
})
|
|
421
|
+
if (item.startsWith('o_delete_')) {
|
|
422
|
+
console.warn(`adding click to ${item}`)
|
|
423
|
+
$(`#${item}`).click(function () {
|
|
424
|
+
console.warn(`clicked ${item}`);
|
|
425
|
+
const keys = item.replace('o_delete_', '').split('-');
|
|
426
|
+
const model = models[keys[0]]?.model.model;
|
|
427
|
+
const option = keys[1];
|
|
428
|
+
const sOptions = models[keys[0]]?.setOptions || {};
|
|
429
|
+
const options = models[keys[0]]?.setOptions?.options || {};
|
|
430
|
+
//options[option] = '##REMOVE##';
|
|
431
|
+
console.warn(`clicked ${item} - options are ${JSON.stringify(options)}`);
|
|
432
|
+
delete options[option];
|
|
433
|
+
updateLocalConfigItems(model, sOptions || {}, true);
|
|
434
|
+
showLocalData();
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
if (item.startsWith('l_delete_')) $(`#${item}`).click(function () {
|
|
438
|
+
const keys = item.replace('l_delete_', '').split('-');
|
|
439
|
+
const model = models[keys[0]]?.model.model;
|
|
440
|
+
const option = keys[1];
|
|
441
|
+
const options = models[keys[0]].setOptions;
|
|
442
|
+
options[option] = '##REMOVE##';
|
|
443
|
+
console.warn(`clicked ${item} - options are ${JSON.stringify(options)}`);
|
|
444
|
+
updateLocalConfigItems(model, options || {}, true)
|
|
445
|
+
delete options[option];
|
|
446
|
+
showLocalData();
|
|
447
|
+
})
|
|
448
|
+
if (item.startsWith('d_disen_')) {
|
|
449
|
+
console.warn(`adding click to ${item}`)
|
|
450
|
+
$(`#${item}`).click(function () {
|
|
451
|
+
console.warn(`clicked ${item}`);
|
|
452
|
+
const keys = item.replace('d_disen_', '').split('-');
|
|
453
|
+
const model = models[keys[0]];
|
|
454
|
+
const device = model.devices.find( (d) => d.native.id === keys[1]);
|
|
455
|
+
swapActive(keys[1]);
|
|
456
|
+
device.common.deactivated = !device.common.deactivated
|
|
457
|
+
showLocalData();
|
|
346
458
|
|
|
347
459
|
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (item.startsWith('d_delall_')) {
|
|
463
|
+
console.warn(`adding click to ${item}`)
|
|
464
|
+
$(`#${item}`).click(function () {
|
|
465
|
+
console.warn(`clicked ${item}`);
|
|
466
|
+
const keys = item.replace('d_delall_', '').split('-');
|
|
467
|
+
const model = models[keys[0]];
|
|
468
|
+
const device = model.devices.find( (d) => d.native.id === keys[1]);
|
|
469
|
+
deleteConfirmation(keys[1], device.common.name, keys[1], models[keys[0]].devices.count <=1 ? models[keys[0]]?.model.model : undefined);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
348
473
|
}
|
|
349
474
|
|
|
350
475
|
/////
|
|
@@ -372,6 +497,7 @@ function getCard(dev) {
|
|
|
372
497
|
rooms.push(dev.rooms[r]);
|
|
373
498
|
}
|
|
374
499
|
}
|
|
500
|
+
const NoInterviewIcon = dev.info?.device?.interviewstate != 'SUCCESSFUL' ? `<div class="col tool"><i class="material-icons icon-red">perm_device_information</i></div>` : ``;
|
|
375
501
|
const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
|
|
376
502
|
const rid = id.split('.').join('_');
|
|
377
503
|
const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type_url}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
|
|
@@ -395,9 +521,15 @@ function getCard(dev) {
|
|
|
395
521
|
${roomInfo}
|
|
396
522
|
</ul>
|
|
397
523
|
</div>`,
|
|
398
|
-
deactBtn = `<button name="swapactive" class="right btn-flat btn-small tooltipped" title="${(isActive ? '
|
|
399
|
-
debugBtn = `<button name="swapdebug" class="right btn-flat btn-small tooltipped" title="${(isDebug > -1 ? (isDebug > 0) ?'
|
|
400
|
-
infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : ''
|
|
524
|
+
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>`,
|
|
525
|
+
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>`,
|
|
526
|
+
infoBtn = (nwk) ? `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>` : '',
|
|
527
|
+
reconfigureButton = dev.info.mapped.hasConfigure ? `<button name="reconfigure" class="right btn-flat btn-small tooltipped" title="reconfigure">
|
|
528
|
+
<i class="material-icons icon-red">sync</i>
|
|
529
|
+
</button>` : ``,
|
|
530
|
+
groupButton = dev.info?.device?.isGroupable ? ` <button name="edit" class="right btn-flat btn-small tooltipped" title="edit group membership">
|
|
531
|
+
<i class="material-icons icon-black">group_work</i>
|
|
532
|
+
</button>` : ``;
|
|
401
533
|
|
|
402
534
|
const dashCard = getDashCard(dev);
|
|
403
535
|
const card = `<div id="${id}" class="device">
|
|
@@ -407,6 +539,7 @@ function getCard(dev) {
|
|
|
407
539
|
<div class="card-content zcard">
|
|
408
540
|
<div class="flip" style="cursor: pointer">
|
|
409
541
|
<span class="top right small" style="border-radius: 50%">
|
|
542
|
+
${NoInterviewIcon}
|
|
410
543
|
${battery}
|
|
411
544
|
<!--${lq}-->
|
|
412
545
|
${status}
|
|
@@ -421,20 +554,15 @@ function getCard(dev) {
|
|
|
421
554
|
<div class="card-action">
|
|
422
555
|
<div class="card-reveal-buttons">
|
|
423
556
|
${infoBtn}
|
|
424
|
-
|
|
425
557
|
<span class="left fw_info"></span>
|
|
426
|
-
<button name="delete" class="right btn-flat btn-small tooltipped" title="
|
|
558
|
+
<button name="delete" class="right btn-flat btn-small tooltipped" title="delete device">
|
|
427
559
|
<i class="material-icons icon-red">delete</i>
|
|
428
560
|
</button>
|
|
429
|
-
|
|
561
|
+
${groupButton}
|
|
562
|
+
<button name="swapimage" class="right btn-flat btn-small tooltipped" title="edit device options">
|
|
430
563
|
<i class="material-icons icon-black">edit</i>
|
|
431
564
|
</button>
|
|
432
|
-
|
|
433
|
-
<i class="material-icons icon-black">image</i>
|
|
434
|
-
</button>
|
|
435
|
-
<button name="reconfigure" class="right btn-flat btn-small tooltipped" title="Reconfigure">
|
|
436
|
-
<i class="material-icons icon-red">sync</i>
|
|
437
|
-
</button>
|
|
565
|
+
${reconfigureButton}
|
|
438
566
|
${deactBtn}
|
|
439
567
|
${debugBtn}
|
|
440
568
|
</div>
|
|
@@ -469,7 +597,7 @@ function getCoordinatorCard(dev) {
|
|
|
469
597
|
<li><span class="label">ZHC / ZH:</span><span>${coordinatorinfo.converters} / ${coordinatorinfo.herdsman}</span></li>
|
|
470
598
|
</ul>
|
|
471
599
|
</div>`,
|
|
472
|
-
permitJoinBtn = '<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>',
|
|
600
|
+
permitJoinBtn = '<div class="col tool"><button name="joinCard" class="waves-effect btn-small btn-flat right hoverable green tooltipped" title="open network"><i class="material-icons icon-green">leak_add</i></button></div>',
|
|
473
601
|
//permitJoinBtn = `<div class="col tool"><button name="join" class="btn-floating-sml waves-effect waves-light right hoverable green><i class="material-icons">leak_add</i></button></div>`,
|
|
474
602
|
card = `<div id="${id}" class="device">
|
|
475
603
|
<div class="card hoverable">
|
|
@@ -522,6 +650,7 @@ function getGroupCard(dev) {
|
|
|
522
650
|
;
|
|
523
651
|
info = info.concat(` ${roomInfo}</ul>
|
|
524
652
|
</div>`);
|
|
653
|
+
const infoBtn = `<button name="info" class="left btn-flat btn-small"><i class="material-icons icon-blue">info</i></button>`;
|
|
525
654
|
const image = `<img src="${dev.common.icon}" width="64px" onerror="this.onerror=null;this.src='img/unavailable.png';">`;
|
|
526
655
|
const dashCard = getDashCard(dev, dev.common.icon, memberCount > 0);
|
|
527
656
|
const card = `<div id="${id}" class="device group">
|
|
@@ -543,14 +672,15 @@ function getGroupCard(dev) {
|
|
|
543
672
|
</div>
|
|
544
673
|
<div class="card-action">
|
|
545
674
|
<div class="card-reveal-buttons">
|
|
546
|
-
|
|
547
|
-
|
|
675
|
+
${infoBtn}
|
|
676
|
+
<button name="deletegrp" class="right btn-flat btn-small tooltipped" title="delete group">
|
|
677
|
+
<i class="material-icons icon-red">delete</i>
|
|
548
678
|
</button>
|
|
549
|
-
<button name="editgrp" class="right btn-flat btn-small">
|
|
550
|
-
<i class="material-icons
|
|
679
|
+
<button name="editgrp" class="right btn-flat btn-small tooltipped" title="edit group members">
|
|
680
|
+
<i class="material-icons">group_work</i>
|
|
551
681
|
</button>
|
|
552
|
-
<button name="swapimage" class="right btn-flat btn-small tooltipped" title="
|
|
553
|
-
<i class="material-icons icon-black">
|
|
682
|
+
<button name="swapimage" class="right btn-flat btn-small tooltipped" title="edit group options">
|
|
683
|
+
<i class="material-icons icon-black">edit</i>
|
|
554
684
|
</button>
|
|
555
685
|
</div>
|
|
556
686
|
</div>
|
|
@@ -580,10 +710,11 @@ function getDashCard(dev, groupImage, groupstatus) {
|
|
|
580
710
|
rooms = [],
|
|
581
711
|
lang = systemLang || 'en';
|
|
582
712
|
const paired = (dev.paired) ? '' : '<i class="material-icons right">leak_remove</i>';
|
|
583
|
-
const permitJoinBtn = dev.
|
|
584
|
-
const device_queryBtn = dev.
|
|
713
|
+
const permitJoinBtn = dev.info?.device?.type == 'EndDevice' || dev.common.type == 'group' ? '' : `<div class="col tool"><button name="joinCard" class="waves-effect btn-small btn-flat right hoverable green tooltipped" title="open network on device"><i class="material-icons icon-green">leak_add</i></button></div>`;
|
|
714
|
+
const device_queryBtn = dev.info?.device?.type == 'EndDevice' || dev.common.type == 'group' ? '' : `<div class="col tool"><button name="deviceQuery" class="waves-effect btn-small btn-flat right hoverable green tooltipped" title="trigger device query"><i class="material-icons icon-green">play_for_work</i></button></div>`;
|
|
585
715
|
const rid = id.split('.').join('_');
|
|
586
716
|
const modelUrl = (!type) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${type}.html" target="_blank" rel="noopener noreferrer">${type}</a>`;
|
|
717
|
+
const NoInterviewIcon = (dev.info?.device?.interviewstate != 'SUCCESSFUL' && dev.common.type != 'group') ? `<div class="col tool"><i class="material-icons icon-red">perm_device_information</i></div>` : ``;
|
|
587
718
|
const image = `<img src="${img_src}" width="64px" onerror="this.onerror=null;this.src='img/unavailable.png';">`,
|
|
588
719
|
nwk = (dev.info && dev.info.device) ? dev.info.device.nwk : undefined,
|
|
589
720
|
battery_cls = getBatteryCls(dev.battery),
|
|
@@ -648,6 +779,7 @@ function getDashCard(dev, groupImage, groupstatus) {
|
|
|
648
779
|
</span>
|
|
649
780
|
<div class="flip">
|
|
650
781
|
<span class="top right small" style="border-radius: 50%">
|
|
782
|
+
${NoInterviewIcon}
|
|
651
783
|
${idleTime}
|
|
652
784
|
${battery}
|
|
653
785
|
${lq}
|
|
@@ -793,7 +925,7 @@ function showDevInfo(id) {
|
|
|
793
925
|
// section Confirmations
|
|
794
926
|
//
|
|
795
927
|
////
|
|
796
|
-
function deleteConfirmation(id, name) {
|
|
928
|
+
function deleteConfirmation(id, name, dev, model) {
|
|
797
929
|
const text = translateWord('Do you really want to delete device') + ' "' + name + '" (' + id + ')?';
|
|
798
930
|
$('#modaldelete').find('p').text(text);
|
|
799
931
|
$('#force').prop('checked', false);
|
|
@@ -801,7 +933,7 @@ function deleteConfirmation(id, name) {
|
|
|
801
933
|
$('#modaldelete a.btn[name=\'yes\']').unbind('click');
|
|
802
934
|
$('#modaldelete a.btn[name=\'yes\']').click(() => {
|
|
803
935
|
const force = $('#force').prop('checked');
|
|
804
|
-
deleteZigbeeDevice(id, force);
|
|
936
|
+
deleteZigbeeDevice(id, force, dev, model);
|
|
805
937
|
});
|
|
806
938
|
$('#modaldelete').modal('open');
|
|
807
939
|
Materialize.updateTextFields();
|
|
@@ -856,7 +988,7 @@ function EndPointIDfromEndPoint(ep) {
|
|
|
856
988
|
|
|
857
989
|
|
|
858
990
|
|
|
859
|
-
function
|
|
991
|
+
function editGroupMembers(id, name) {
|
|
860
992
|
|
|
861
993
|
function updateGroupables(groupables) {
|
|
862
994
|
const html = [];
|
|
@@ -923,8 +1055,8 @@ function GenerateGroupChange(oldmembers, newmembers) {
|
|
|
923
1055
|
return grpchng;
|
|
924
1056
|
}
|
|
925
1057
|
|
|
926
|
-
function deleteZigbeeDevice(id, force) {
|
|
927
|
-
sendToWrapper(namespace, 'deleteZigbeeDevice', {id: id, force: force}, function (msg) {
|
|
1058
|
+
function deleteZigbeeDevice(id, force, devOpts, modelOpts) {
|
|
1059
|
+
sendToWrapper(namespace, 'deleteZigbeeDevice', {id: id, force: force, dev:devOpts, model:modelOpts}, function (msg) {
|
|
928
1060
|
closeWaitingDialog();
|
|
929
1061
|
if (msg) {
|
|
930
1062
|
if (msg.error) {
|
|
@@ -934,7 +1066,7 @@ function deleteZigbeeDevice(id, force) {
|
|
|
934
1066
|
}
|
|
935
1067
|
}
|
|
936
1068
|
});
|
|
937
|
-
showWaitingDialog('Device is being removed',
|
|
1069
|
+
showWaitingDialog('Device is being removed', 30);
|
|
938
1070
|
}
|
|
939
1071
|
|
|
940
1072
|
|
|
@@ -1040,7 +1172,7 @@ function showDevices() {
|
|
|
1040
1172
|
return room;
|
|
1041
1173
|
}
|
|
1042
1174
|
}).filter((item) => item != undefined));
|
|
1043
|
-
console.warn(`rooms is ${JSON.stringify(allRooms)}`);
|
|
1175
|
+
//console.warn(`rooms is ${JSON.stringify(allRooms)}`);
|
|
1044
1176
|
const roomSelector = $('#room-filter');
|
|
1045
1177
|
roomSelector.empty();
|
|
1046
1178
|
roomSelector.append(`<li class="device-order-item" data-type="All" tabindex="0"><a class="translate" data-lang="All">All</a></li>`);
|
|
@@ -1092,7 +1224,7 @@ function showDevices() {
|
|
|
1092
1224
|
const dev_block = $(this).parents('div.device');
|
|
1093
1225
|
const id = getDevId(dev_block);
|
|
1094
1226
|
const name = getDevName(dev_block);
|
|
1095
|
-
|
|
1227
|
+
editGroupMembers(id, name);
|
|
1096
1228
|
});
|
|
1097
1229
|
$('.card-reveal-buttons button[name=\'swapdebug\']').click(function () {
|
|
1098
1230
|
const dev_block = $(this).parents('div.device');
|
|
@@ -1104,7 +1236,7 @@ function showDevices() {
|
|
|
1104
1236
|
$('.card-reveal-buttons button[name=\'swapimage\']').click(function () {
|
|
1105
1237
|
const dev_block = $(this).parents('div.device');
|
|
1106
1238
|
const id = getDevId(dev_block);
|
|
1107
|
-
|
|
1239
|
+
editDeviceOptions(id, false);
|
|
1108
1240
|
});
|
|
1109
1241
|
|
|
1110
1242
|
$('.card-reveal-buttons button[name=\'editgrp\']').click(function () {
|
|
@@ -1125,12 +1257,6 @@ function showDevices() {
|
|
|
1125
1257
|
sendTo(namespace, 'setState', {id: `${getDevId(dev_block)}.device_query`, val: true}, function (data) {
|
|
1126
1258
|
//console.log(data);
|
|
1127
1259
|
}); });
|
|
1128
|
-
$('#modalpairing a.btn[name=\'extendpairing\']').click(function () {
|
|
1129
|
-
openNetwork();
|
|
1130
|
-
});
|
|
1131
|
-
$('#modalpairing a.btn[name=\'endpairing\']').click(function () {
|
|
1132
|
-
stopPairing();
|
|
1133
|
-
});
|
|
1134
1260
|
$('.card-reveal-buttons button[name=\'info\']').click(function () {
|
|
1135
1261
|
const dev_block = $(this).parents('div.device');
|
|
1136
1262
|
showDevInfo(getDevId(dev_block));
|
|
@@ -1223,22 +1349,22 @@ function letsPairingWithCode(code) {
|
|
|
1223
1349
|
}
|
|
1224
1350
|
|
|
1225
1351
|
function openNetwork() {
|
|
1226
|
-
messages = [];
|
|
1227
1352
|
sendToWrapper(namespace, 'letsPairing', {stop:false}, function (msg) {
|
|
1228
1353
|
if (msg && msg.error) {
|
|
1229
1354
|
showMessage(msg.error, _('Error'));
|
|
1230
1355
|
}
|
|
1231
|
-
else showPairingProcess();
|
|
1356
|
+
//else showPairingProcess();
|
|
1232
1357
|
});
|
|
1233
1358
|
}
|
|
1234
1359
|
|
|
1235
1360
|
function stopPairing() {
|
|
1236
|
-
messages = [];
|
|
1237
1361
|
sendToWrapper(namespace, 'letsPairing', {stop:true}, function (msg) {
|
|
1238
1362
|
if (msg && msg.error) {
|
|
1239
1363
|
showMessage(msg.error, _('Error'));
|
|
1240
1364
|
}
|
|
1241
1365
|
});
|
|
1366
|
+
$('#pairing').html('<i class="material-icons">leak_add</i>');
|
|
1367
|
+
|
|
1242
1368
|
}
|
|
1243
1369
|
|
|
1244
1370
|
function touchlinkReset() {
|
|
@@ -1303,21 +1429,24 @@ async function toggleDebugDevice(id) {
|
|
|
1303
1429
|
}
|
|
1304
1430
|
|
|
1305
1431
|
function updateLocalConfigItems(device, data, global) {
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1432
|
+
if (data != {})
|
|
1433
|
+
sendToWrapper(namespace, 'updateLocalConfigItems', {target: device, data:data, global:global}, function(msg) {
|
|
1434
|
+
if (msg && msg.hasOwnProperty.error) {
|
|
1435
|
+
showMessage(msg.error, _('Error'));
|
|
1436
|
+
}
|
|
1437
|
+
getDevices();
|
|
1438
|
+
});
|
|
1312
1439
|
}
|
|
1313
1440
|
|
|
1314
|
-
async function
|
|
1441
|
+
async function editDeviceOptions(id, isModel) {
|
|
1442
|
+
//console.warn(`selectImageOverride on ${JSON.stringify(id)}`);
|
|
1315
1443
|
|
|
1316
1444
|
// start local functions
|
|
1317
1445
|
function removeOption(k) {
|
|
1446
|
+
const model = dialogData.model;
|
|
1318
1447
|
if (k && device_options.hasOwnProperty(k)) {
|
|
1319
|
-
if (
|
|
1320
|
-
availableOptions.push(device_options[k].key)
|
|
1448
|
+
if (device_options[k].key === 'legacy' || (model && model.options && model.options.includes(device_options[k].key)) && !device_options[k].isCustom)
|
|
1449
|
+
dialogData.availableOptions.push(device_options[k].key)
|
|
1321
1450
|
delete device_options[k];
|
|
1322
1451
|
}
|
|
1323
1452
|
}
|
|
@@ -1330,12 +1459,15 @@ async function selectImageOverride(id) {
|
|
|
1330
1459
|
key = `o${idx++}`;
|
|
1331
1460
|
}
|
|
1332
1461
|
while (device_options.hasOwnProperty(key));
|
|
1333
|
-
device_options[key] = { key:optionName, value:''};
|
|
1334
|
-
idx = availableOptions.indexOf(optionName);
|
|
1335
|
-
if (idx > -1) availableOptions.splice(idx, 1);
|
|
1462
|
+
device_options[key] = { key:optionName, value:'', isCustom:optionName==='custom', expose:getExposeFromOptions(optionName)};
|
|
1463
|
+
idx = dialogData.availableOptions.indexOf(optionName);
|
|
1464
|
+
if (idx > -1 && !device_options[key].isCustom) dialogData.availableOptions.splice(idx, 1);
|
|
1465
|
+
console.warn(`addOption added ${JSON.stringify(device_options)}`)
|
|
1336
1466
|
}
|
|
1337
1467
|
|
|
1468
|
+
|
|
1338
1469
|
function updateOptions(candidates) {
|
|
1470
|
+
console.warn(`update Options with ${JSON.stringify(candidates)}`)
|
|
1339
1471
|
if (candidates.length > 0) {
|
|
1340
1472
|
$('#chooseimage').find('.new_options_available').removeClass('hide');
|
|
1341
1473
|
list2select('#option_Selector', candidates, [], (key, val) => { return val; }, (key, val) => { return val; })
|
|
@@ -1344,54 +1476,123 @@ async function selectImageOverride(id) {
|
|
|
1344
1476
|
$('#chooseimage').find('.new_options_available').addClass('hide');
|
|
1345
1477
|
}
|
|
1346
1478
|
const html_options=[];
|
|
1479
|
+
const checkboxButtons = [];
|
|
1347
1480
|
|
|
1348
1481
|
for (const k of Object.keys(device_options)) {
|
|
1482
|
+
const expose = device_options[k].expose === undefined ? getExposeFromOptions(device_options[k].key) : device_options[k].expose;
|
|
1483
|
+
const disabled = device_options[k]?.isCustom ? '' : 'disabled ';
|
|
1484
|
+
console.warn(`option for ${k} is ${JSON.stringify(device_options[k])}`);
|
|
1349
1485
|
html_options.push(`<div class="row">`);
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1486
|
+
switch (expose.type) {
|
|
1487
|
+
case 'numeric':
|
|
1488
|
+
html_options.push(`<div class="input-field col s5 m5 l5"><input ${disabled}id="option_key_${k}" type="text" class="value" /><label for="option_key_${k}">Option</label></div>`)
|
|
1489
|
+
html_options.push(`<div class="input-field col s5 m5 l5"><input id="option_value_${k}" type="number"${expose.value_min != undefined ? ' min="'+expose.value_min+'"' : ''}${expose.value_max != undefined ? ' max="'+expose.value_max+'"' : ''}${expose.value_step != undefined ? ' step="'+expose.value_step+'"' : ''} class="value" /><label>${expose.label ? expose.label : 'Value'}</label></div>`)
|
|
1490
|
+
break;
|
|
1491
|
+
case 'binary': {
|
|
1492
|
+
html_options.push(`<div class="input-field col s5 m5 l5">
|
|
1493
|
+
<input ${disabled}id="option_key_${k}" type="text" class="value" />
|
|
1494
|
+
<label for="option_key_${k}">Option</label></div>`);
|
|
1495
|
+
const dok = device_options[k];
|
|
1496
|
+
if (dok.vOn=== undefined) dok.vOn = (expose.value_on === undefined ? 'true' : String(expose.value_on));
|
|
1497
|
+
if (dok.vOff=== undefined) dok.vOff = (expose.value_off === undefined ? 'false' : String(expose.value_off));
|
|
1498
|
+
if (dok.value != dok.vOn) dok.value = dok.vOff;
|
|
1499
|
+
html_options.push(`<div class="col s5 m5 l5"><a id="option_value_${k}" class="btn-large value">${dok.value}</a></div>`);
|
|
1500
|
+
checkboxButtons.push(`option_value_${k}`);
|
|
1501
|
+
break;
|
|
1502
|
+
}
|
|
1503
|
+
default:
|
|
1504
|
+
html_options.push(`<div class="input-field col s5 m5 l5"><input ${disabled}id="option_key_${k}" type="text" class="value" /><label for="option_key_${k}">Option</label></div>`)
|
|
1505
|
+
html_options.push(`<div class="input-field col s5 m5 l5"><input id="option_value_${k}" type="text" class="value" /><label for="option_value_${k}">${expose.label ? expose.label : 'Value'}</label></div>`)
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
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>`);
|
|
1353
1509
|
html_options.push(`</div>`)
|
|
1354
1510
|
}
|
|
1355
1511
|
$('#chooseimage').find('.options_grid').html(html_options.join(''));
|
|
1512
|
+
for (const item of checkboxButtons) {
|
|
1513
|
+
$(`#${item}`).unbind('click');
|
|
1514
|
+
$(`#${item}`).click(() => {
|
|
1515
|
+
const key = item.replace('option_value_', '');
|
|
1516
|
+
const dok = device_options[key];
|
|
1517
|
+
const oval = $(`#option_value_${key}`).html();
|
|
1518
|
+
const val = $(`#option_value_${key}`).html()=== dok.vOn ? dok.vOff : dok.vOn;
|
|
1519
|
+
dok.value = val;
|
|
1520
|
+
console.warn(`${item} clicked: ${JSON.stringify(dok)} => ${val} from ${oval}`);
|
|
1521
|
+
$(`#${item}`).html(val);
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1356
1525
|
if (html_options.length > 0) {
|
|
1357
1526
|
for (const k of Object.keys(device_options)) {
|
|
1527
|
+
if (device_options[k].isCustom) $(`#option_key_${k}`).removeClass('disabled')
|
|
1358
1528
|
$(`#option_key_${k}`).val(device_options[k].key);
|
|
1359
|
-
|
|
1529
|
+
if (device_options[k].expose?.type != 'binary') {
|
|
1530
|
+
const value = $(`#option_value_${k}.value`);
|
|
1531
|
+
/* if (value.attr('type') === 'checkbox') {
|
|
1532
|
+
console.warn(`oval for ${k} : ${device_options[k].value}`);
|
|
1533
|
+
value.prop('checked', Boolean(device_options[k].value));
|
|
1534
|
+
}
|
|
1535
|
+
else*/
|
|
1536
|
+
value.val(device_options[k].value);
|
|
1537
|
+
}
|
|
1360
1538
|
$(`#option_rem_${k}`).unbind('click');
|
|
1361
|
-
$(`#option_rem_${k}`).click(() => {
|
|
1539
|
+
$(`#option_rem_${k}`).click(() => {
|
|
1540
|
+
removeOption(k);
|
|
1541
|
+
updateOptions(dialogData.availableOptions);
|
|
1542
|
+
});
|
|
1362
1543
|
}
|
|
1363
1544
|
}
|
|
1364
1545
|
}
|
|
1365
1546
|
|
|
1547
|
+
function getExposeFromOptions(option) {
|
|
1548
|
+
const rv = dialogData.model.optionExposes.find((expose) => expose.name === option);
|
|
1549
|
+
console.warn(`GEFO: ${option} results in ${JSON.stringify(rv)}`);
|
|
1550
|
+
if (rv) return rv;
|
|
1551
|
+
return { type:option === 'legacy' ? 'binary' : 'string' };
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1366
1554
|
function getOptionsFromUI(_do, _so) {
|
|
1367
1555
|
const _no = {};
|
|
1368
1556
|
let changed = false;
|
|
1557
|
+
console.warn(`${changed} : ${JSON.stringify(_do)} - ${JSON.stringify(_no)}`)
|
|
1369
1558
|
for (const k of Object.keys(_do)) {
|
|
1370
1559
|
const key = $(`#option_key_${k}`).val();
|
|
1371
|
-
_do[k].key = key;
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1560
|
+
if (_do[k].isCustom) _do[k].key = key;
|
|
1561
|
+
else if (_do[k].key != key) {
|
|
1562
|
+
console.warn(`_illegal Keys: ${key}, ${_do[k].key}`)
|
|
1563
|
+
continue;
|
|
1375
1564
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1565
|
+
console.warn(`_legal Keys: ${key}, ${_do[k].key}`)
|
|
1566
|
+
if (_do[k].expose?.type === 'binary') {
|
|
1567
|
+
_do[k].value = $(`#option_value_${k}`).html();
|
|
1568
|
+
}
|
|
1569
|
+
else
|
|
1570
|
+
{
|
|
1571
|
+
const val = $(`#option_value_${k}`).val();
|
|
1572
|
+
try {
|
|
1573
|
+
_do[k].value = JSON.parse(val);
|
|
1574
|
+
}
|
|
1575
|
+
catch {
|
|
1576
|
+
_do[k].value = val;
|
|
1577
|
+
}
|
|
1378
1578
|
}
|
|
1379
|
-
if (
|
|
1380
|
-
|
|
1381
|
-
|
|
1579
|
+
if (_do[k].key.length > 0) {
|
|
1580
|
+
console.warn(`dok: ${_do[k].key} : ${_do[k].value}`);
|
|
1581
|
+
_no[key] = _do[k].value;
|
|
1582
|
+
changed |= (_no[key] != _so[key]);
|
|
1382
1583
|
}
|
|
1383
1584
|
}
|
|
1384
1585
|
changed |= (Object.keys(_no).length != Object.keys(_so).length);
|
|
1385
|
-
console.warn(`${changed} : ${JSON.stringify(
|
|
1586
|
+
console.warn(`${changed ? 'changed': 'unchanged'} : ${JSON.stringify(_so)} - ${JSON.stringify(_no)}`)
|
|
1386
1587
|
if (changed) return _no;
|
|
1387
1588
|
return undefined;
|
|
1388
1589
|
}
|
|
1389
1590
|
|
|
1390
|
-
function updateImageSelection(
|
|
1391
|
-
const default_icon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
|
|
1392
|
-
if (
|
|
1393
|
-
imagedata.unshift( { file:'none', name:'default', data:
|
|
1394
|
-
imagedata.unshift( { file:'current', name:'current', data:
|
|
1591
|
+
function updateImageSelection(dData, imagedata) {
|
|
1592
|
+
// const default_icon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
|
|
1593
|
+
if (dData.legacyIcon) imagedata.unshift( { file:dData.legacyIcon, name:'legacy', data:dData.legacyIcon});
|
|
1594
|
+
imagedata.unshift( { file:'none', name:'default', data:dData.defaultIcon});
|
|
1595
|
+
imagedata.unshift( { file:'current', name:'current', data:dData.icon});
|
|
1395
1596
|
|
|
1396
1597
|
list2select('#images', imagedata, selectItems,
|
|
1397
1598
|
function (key, image) {
|
|
@@ -1414,39 +1615,65 @@ async function selectImageOverride(id) {
|
|
|
1414
1615
|
const device_options = {};
|
|
1415
1616
|
const received_options = {};
|
|
1416
1617
|
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
|
|
1618
|
+
const dialogData = {};
|
|
1619
|
+
|
|
1620
|
+
if (isModel) {
|
|
1621
|
+
const model = id.model;
|
|
1622
|
+
dialogData.model = model;
|
|
1623
|
+
dialogData.availableOptions = model.options.slice() || [];
|
|
1624
|
+
dialogData.availableOptions.push('custom');
|
|
1625
|
+
if (model.hasLegacyDef) dialogData.availableOptions.push('legacy');
|
|
1626
|
+
dialogData.setOptions = {};
|
|
1627
|
+
for (const k in Object.keys(id.setOptions))
|
|
1628
|
+
if (k == 'icon' || k == 'name') continue;
|
|
1629
|
+
else dialogData.setOptions[k] = id.setOptions[k];
|
|
1630
|
+
dialogData.name = id.setOptions.name || id.name || 'unset';
|
|
1631
|
+
dialogData.icon = id.setOptions.icon || model.icon || 'img/dummyDevice.jpg';
|
|
1632
|
+
dialogData.legacyIcon = id.devices[0].legacyIcon;
|
|
1633
|
+
id = id.model.model;
|
|
1634
|
+
} else
|
|
1635
|
+
{
|
|
1636
|
+
const dev = devices.find((d) => d._id == id);
|
|
1637
|
+
dialogData.model = dev.info.mapped;
|
|
1638
|
+
dialogData.availableOptions = (dev.info.mapped ? dev.info.mapped.options.slice() || []:[]);
|
|
1639
|
+
dialogData.name = dev.common.name;
|
|
1640
|
+
dialogData.icon = dev.common.icon || dev.icon;
|
|
1641
|
+
dialogData.default_icon = (dev.common.type === 'group' ? dev.common.modelIcon : `img/${dev.common.type.replace(/\//g, '-')}.png`);
|
|
1642
|
+
dialogData.legacyIcon = dev.legacyIcon;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
const imghtml = `<img src="${dialogData.icon}" width="80px">`;
|
|
1420
1646
|
//console.error(imghtml)
|
|
1421
1647
|
const selectItems= [''];
|
|
1422
|
-
$('#chooseimage').find('input[id=\'d_name\']').val(
|
|
1648
|
+
$('#chooseimage').find('input[id=\'d_name\']').val(dialogData.name);
|
|
1423
1649
|
$('#chooseimage').find('.currentIcon').html(imghtml);
|
|
1424
1650
|
$('#option_add_1084').unbind('click');
|
|
1425
1651
|
$('#option_add_1084').click(() => {
|
|
1426
1652
|
getOptionsFromUI(device_options, received_options);
|
|
1427
1653
|
addOption();
|
|
1428
|
-
updateOptions(availableOptions)
|
|
1654
|
+
updateOptions(dialogData.availableOptions);
|
|
1429
1655
|
});
|
|
1430
1656
|
|
|
1431
1657
|
|
|
1432
1658
|
|
|
1433
1659
|
sendToWrapper(namespace, 'getLocalImages', {}, function(msg) {
|
|
1434
1660
|
if (msg && msg.imageData) {
|
|
1435
|
-
updateImageSelection(
|
|
1661
|
+
updateImageSelection(dialogData , msg.imageData);
|
|
1436
1662
|
|
|
1437
1663
|
$('#chooseimage a.btn[name=\'save\']').unbind('click');
|
|
1438
1664
|
$('#chooseimage a.btn[name=\'save\']').click(() => {
|
|
1439
1665
|
const image = $('#chooseimage').find('#images option:selected').val();
|
|
1440
|
-
const global = $('#chooseimage').find('#globaloverride').prop('checked');
|
|
1666
|
+
//const global = $('#chooseimage').find('#globaloverride').prop('checked');
|
|
1441
1667
|
const name = $('#chooseimage').find('input[id=\'d_name\']').val();
|
|
1442
1668
|
const data = {};
|
|
1443
1669
|
if (image != 'current') data.icon= image;
|
|
1444
|
-
if (name !=
|
|
1445
|
-
|
|
1670
|
+
if (name != dialogData.name) data.name = name;
|
|
1671
|
+
const changedOptions = getOptionsFromUI(device_options, received_options);
|
|
1672
|
+
if (changedOptions != undefined) data.options = changedOptions;
|
|
1446
1673
|
|
|
1447
|
-
updateLocalConfigItems(id, data,
|
|
1674
|
+
updateLocalConfigItems(id, data, isModel);
|
|
1448
1675
|
});
|
|
1449
|
-
sendToWrapper(namespace, 'getLocalConfigItems', { target:id, global:
|
|
1676
|
+
sendToWrapper(namespace, 'getLocalConfigItems', { target:id, global:isModel, key:'options' }, function (msg) {
|
|
1450
1677
|
if (msg) {
|
|
1451
1678
|
if (msg.error) showMessage(msg.error, '_Error');
|
|
1452
1679
|
Object.keys(device_options).forEach(key => delete device_options[key]);
|
|
@@ -1455,15 +1682,15 @@ async function selectImageOverride(id) {
|
|
|
1455
1682
|
let cnt = 1;
|
|
1456
1683
|
for (const key in msg.options)
|
|
1457
1684
|
{
|
|
1458
|
-
const idx = availableOptions.indexOf(key);
|
|
1685
|
+
const idx = dialogData.availableOptions.indexOf(key);
|
|
1459
1686
|
console.warn(`key ${key} : index : ${idx}`);
|
|
1460
|
-
if (idx > -1) availableOptions.splice(idx,1);
|
|
1687
|
+
if (idx > -1) dialogData.availableOptions.splice(idx,1);
|
|
1461
1688
|
received_options[key]=msg.options[key];
|
|
1462
1689
|
device_options[`o${cnt}`] = { key:key, value:msg.options[key]}
|
|
1463
1690
|
cnt++;
|
|
1464
1691
|
}
|
|
1465
1692
|
}
|
|
1466
|
-
updateOptions(availableOptions);
|
|
1693
|
+
updateOptions(dialogData.availableOptions);
|
|
1467
1694
|
} else showMessage('callback without message');
|
|
1468
1695
|
$('#chooseimage').modal('open');
|
|
1469
1696
|
Materialize.updateTextFields();
|
|
@@ -1580,6 +1807,7 @@ function HtmlFromOutDebugMessages(messages, devID, filter) {
|
|
|
1580
1807
|
}
|
|
1581
1808
|
|
|
1582
1809
|
function displayDebugMessages(msg) {
|
|
1810
|
+
console.warn('displayDebugMessages called with '+ JSON.stringify(msg));
|
|
1583
1811
|
const buttonNames = [];
|
|
1584
1812
|
const idButtons = [];
|
|
1585
1813
|
if (msg.byId) {
|
|
@@ -1785,7 +2013,9 @@ function getDevices() {
|
|
|
1785
2013
|
getBinding();
|
|
1786
2014
|
}
|
|
1787
2015
|
updateStartButton();
|
|
2016
|
+
displayDebugMessages(debugMessages);
|
|
1788
2017
|
showDevices();
|
|
2018
|
+
LocalDataDisplayValues.sortedKeys = Object.keys(models);
|
|
1789
2019
|
showLocalData();
|
|
1790
2020
|
UpdateAdapterAlive(true)
|
|
1791
2021
|
}
|
|
@@ -1826,9 +2056,10 @@ function extractDevicesData(msg) {
|
|
|
1826
2056
|
}
|
|
1827
2057
|
else
|
|
1828
2058
|
debugDevices = [];
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2059
|
+
|
|
2060
|
+
if (msg.deviceDebugData) {
|
|
2061
|
+
debugMessages = { byId: msg.deviceDebugData };
|
|
2062
|
+
displayDebugMessages(debugMessages);
|
|
1832
2063
|
}
|
|
1833
2064
|
if (msg.models) models = msg.models;
|
|
1834
2065
|
lockout.isActive = false;
|
|
@@ -1946,7 +2177,7 @@ function load(settings, onChange) {
|
|
|
1946
2177
|
getComPorts(onChange);
|
|
1947
2178
|
|
|
1948
2179
|
//dialog = new MatDialog({EndingTop: '50%'});
|
|
1949
|
-
const keepAliveHandle = startKeepalive();
|
|
2180
|
+
//const keepAliveHandle = startKeepalive();
|
|
1950
2181
|
keepAlive(() => {
|
|
1951
2182
|
getDevices();
|
|
1952
2183
|
getNamedColors();
|
|
@@ -1955,6 +2186,10 @@ function load(settings, onChange) {
|
|
|
1955
2186
|
groups = data.groups || {};
|
|
1956
2187
|
//showGroups();
|
|
1957
2188
|
});
|
|
2189
|
+
sendToWrapper(namespace, 'getLibData', {key: 'cidList'}, function (data) {
|
|
2190
|
+
cidList = data.list;
|
|
2191
|
+
});
|
|
2192
|
+
|
|
1958
2193
|
})
|
|
1959
2194
|
|
|
1960
2195
|
//getDebugMessages();
|
|
@@ -2008,8 +2243,10 @@ function load(settings, onChange) {
|
|
|
2008
2243
|
});
|
|
2009
2244
|
$('#pairing').click(function () {
|
|
2010
2245
|
if (!$('#pairing').hasClass('pulse')) {
|
|
2246
|
+
messages = [];
|
|
2011
2247
|
openNetwork();
|
|
2012
|
-
}
|
|
2248
|
+
}
|
|
2249
|
+
showPairingProcess();
|
|
2013
2250
|
});
|
|
2014
2251
|
|
|
2015
2252
|
$('#refresh').click(function () {
|
|
@@ -2024,6 +2261,10 @@ function load(settings, onChange) {
|
|
|
2024
2261
|
resetConfirmation();
|
|
2025
2262
|
});
|
|
2026
2263
|
|
|
2264
|
+
$('#restore-backup-btn').click(function () {
|
|
2265
|
+
selectBackup();
|
|
2266
|
+
});
|
|
2267
|
+
|
|
2027
2268
|
$('#deleteNVRam-btn').click(function () {
|
|
2028
2269
|
deleteNvBackupConfirmation();
|
|
2029
2270
|
});
|
|
@@ -2091,6 +2332,65 @@ function load(settings, onChange) {
|
|
|
2091
2332
|
$('#device-filter-btn').text($(this).text());
|
|
2092
2333
|
doFilter();
|
|
2093
2334
|
});
|
|
2335
|
+
$('#model-search').keyup(function (event) {
|
|
2336
|
+
LocalDataDisplayValues.searchVal = event.target.value.toLowerCase();
|
|
2337
|
+
if (!LocalDataDisplayValues.searchTimeout)
|
|
2338
|
+
LocalDataDisplayValues.searchTimeout = setTimeout(() => { LocalDataDisplayValues.searchTimeout = null; showLocalData(); }, 250);
|
|
2339
|
+
});
|
|
2340
|
+
$('#model-sort a').click(function () {
|
|
2341
|
+
const t = $(this).text();
|
|
2342
|
+
$('#model-sort-btn').text(t);
|
|
2343
|
+
switch (t) {
|
|
2344
|
+
case 'by type':
|
|
2345
|
+
LocalDataDisplayValues.sortMethod = function(a,b) {
|
|
2346
|
+
if (models[a].model?.type == models[b].model?.type) return (models[a].model?.model > models[b].model?.model ? 1 : -1);
|
|
2347
|
+
return (models[a].model?.type > models[b].model?.type ? 1 : -1);
|
|
2348
|
+
};
|
|
2349
|
+
break;
|
|
2350
|
+
|
|
2351
|
+
case 'by device count':
|
|
2352
|
+
LocalDataDisplayValues.sortMethod = function(a,b) {
|
|
2353
|
+
if (models[a].setOptions?.length == models[b].setOptions?.length) return (models[a].model?.model > models[b].model?.model ? 1 : -1);
|
|
2354
|
+
return (models[a].setOptions?.length > models[b].setOptions?.length?1:-1);
|
|
2355
|
+
};
|
|
2356
|
+
break;
|
|
2357
|
+
case 'by option count':
|
|
2358
|
+
LocalDataDisplayValues.sortMethod = function(a,b) {
|
|
2359
|
+
if (models[a].devices?.length == models[b].devices?.length) return (models[a].model?.model > models[b].model?.model ? 1 : -1);
|
|
2360
|
+
return (models[a].devices?.length > models[b].devices?.length ? 1 : -1);
|
|
2361
|
+
};
|
|
2362
|
+
break;
|
|
2363
|
+
default:
|
|
2364
|
+
LocalDataDisplayValues.sortMethod = undefined;
|
|
2365
|
+
}
|
|
2366
|
+
showLocalData();
|
|
2367
|
+
});
|
|
2368
|
+
$('#refresh_models_btn').click(function () {
|
|
2369
|
+
getDevices();
|
|
2370
|
+
});
|
|
2371
|
+
$('#model-filter a').click(function () {
|
|
2372
|
+
const t = $(this).text();
|
|
2373
|
+
$('#model-filter-btn').text(t);
|
|
2374
|
+
switch (t) {
|
|
2375
|
+
case 'Groups':
|
|
2376
|
+
LocalDataDisplayValues.filterMethod = function(a) { return models[a].model.model== 'group'};
|
|
2377
|
+
break;
|
|
2378
|
+
case 'Routers':
|
|
2379
|
+
LocalDataDisplayValues.filterMethod = function(a) { return models[a].model.type == 'Router'};
|
|
2380
|
+
break;
|
|
2381
|
+
case 'End Devices':
|
|
2382
|
+
LocalDataDisplayValues.filterMethod = function(a) { return models[a].model.type == 'EndDevice'};
|
|
2383
|
+
break;
|
|
2384
|
+
case 'with options':
|
|
2385
|
+
LocalDataDisplayValues.filterMethod = function(a) { return models[a].setOptions && Object.keys(models[a].setOptions).length > 0 };
|
|
2386
|
+
break;
|
|
2387
|
+
case 'without options':
|
|
2388
|
+
LocalDataDisplayValues.filterMethod = function(a) { return !(models[a].setOptions && Object.keys(models[a].setOptions).length > 0) };
|
|
2389
|
+
break;
|
|
2390
|
+
default: LocalDataDisplayValues.filterMethod = undefined;
|
|
2391
|
+
}
|
|
2392
|
+
showLocalData();
|
|
2393
|
+
});
|
|
2094
2394
|
});
|
|
2095
2395
|
|
|
2096
2396
|
const text = $('#pairing').attr('data-tooltip');
|
|
@@ -2142,6 +2442,14 @@ function showPairingProcess(noextrabuttons) {
|
|
|
2142
2442
|
dismissible: false
|
|
2143
2443
|
});
|
|
2144
2444
|
|
|
2445
|
+
$('#modalpairing a.btn[name=\'extendpairing\']').unbind('click');
|
|
2446
|
+
$('#modalpairing a.btn[name=\'extendpairing\']').click(function () {
|
|
2447
|
+
openNetwork();
|
|
2448
|
+
});
|
|
2449
|
+
$('#modalpairing a.btn[name=\'endpairing\']').unbind('click');
|
|
2450
|
+
$('#modalpairing a.btn[name=\'endpairing\']').click(function () {
|
|
2451
|
+
stopPairing();
|
|
2452
|
+
});
|
|
2145
2453
|
if (noextrabuttons) {
|
|
2146
2454
|
$('#modalpairing').find('.endpairing').addClass('hide');
|
|
2147
2455
|
$('#modalpairing').find('.extendpairing').addClass('hide');
|
|
@@ -2269,6 +2577,7 @@ socket.emit('subscribeObjects', namespace + '.*');
|
|
|
2269
2577
|
|
|
2270
2578
|
// react to changes
|
|
2271
2579
|
socket.on('stateChange', function (id, state) {
|
|
2580
|
+
UpdateAdapterAlive(true);
|
|
2272
2581
|
// only watch our own states
|
|
2273
2582
|
if (id.substring(0, namespaceLen) !== namespace) return;
|
|
2274
2583
|
if (state) {
|
|
@@ -2304,6 +2613,9 @@ socket.on('stateChange', function (id, state) {
|
|
|
2304
2613
|
isHerdsmanRunning = false;
|
|
2305
2614
|
updateStartButton();
|
|
2306
2615
|
}
|
|
2616
|
+
if (state.val === 'Closing network.') {
|
|
2617
|
+
getDevices();
|
|
2618
|
+
}
|
|
2307
2619
|
}
|
|
2308
2620
|
} else {
|
|
2309
2621
|
const devId = getDevId(id);
|
|
@@ -2331,6 +2643,7 @@ socket.on('stateChange', function (id, state) {
|
|
|
2331
2643
|
|
|
2332
2644
|
|
|
2333
2645
|
socket.on('objectChange', function (id, obj) {
|
|
2646
|
+
UpdateAdapterAlive(true);
|
|
2334
2647
|
if (id.substring(0, namespaceLen) !== namespace) return;
|
|
2335
2648
|
if (obj && obj.type == 'device') { // && obj.common.type !== 'group') {
|
|
2336
2649
|
updateDevice(id);
|
|
@@ -2341,6 +2654,7 @@ socket.on('objectChange', function (id, obj) {
|
|
|
2341
2654
|
if (elems.length === 3) {
|
|
2342
2655
|
removeDevice(id);
|
|
2343
2656
|
showDevices();
|
|
2657
|
+
showLocalData();
|
|
2344
2658
|
}
|
|
2345
2659
|
}
|
|
2346
2660
|
});
|
|
@@ -2408,7 +2722,7 @@ function showNetworkMap(devices, map) {
|
|
|
2408
2722
|
node.label = 'Coordinator';
|
|
2409
2723
|
// delete node.color;
|
|
2410
2724
|
}
|
|
2411
|
-
console.warn(`node for device ${JSON.stringify(node)}`)
|
|
2725
|
+
//console.warn(`node for device ${JSON.stringify(node)}`)
|
|
2412
2726
|
return node;
|
|
2413
2727
|
};
|
|
2414
2728
|
|
|
@@ -3257,6 +3571,42 @@ function resetConfirmation() {
|
|
|
3257
3571
|
});
|
|
3258
3572
|
}
|
|
3259
3573
|
|
|
3574
|
+
|
|
3575
|
+
function selectBackup() {
|
|
3576
|
+
sendToWrapper(namespace, 'listbackups', {}, function(msg) {
|
|
3577
|
+
const candidates = {};
|
|
3578
|
+
for (const fn of msg.files) {
|
|
3579
|
+
const m = fn.matchAll(/backup_([0-9]+)_([0-9]+)_([0-9]+)-([0-9]+)_([0-9]+)_([0-9]+)/gm);
|
|
3580
|
+
console.warn(`m is ${JSON.stringify(m)}`);
|
|
3581
|
+
if (m) {
|
|
3582
|
+
candidates[`${m[3]}.${m[2]}.${m[1]} ${m[4]}:${m[5]}`] = fn;
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
console.warn('candidates is ' + JSON.stringify(candidates));
|
|
3586
|
+
list2select('#backup_Selector', msg.files, [], (key, val) => { return val; }, (key, val) => { return val; })
|
|
3587
|
+
$('#modalrestore').modal('open');
|
|
3588
|
+
const btn = $('#modalrestore .modal-content a.btn-large');
|
|
3589
|
+
btn.unbind('click')
|
|
3590
|
+
btn.click(function (e) {
|
|
3591
|
+
const name = $('#backup_Selector').val();
|
|
3592
|
+
console.warn(` filename is ${name}`);
|
|
3593
|
+
$('#modalrestore').modal('close');
|
|
3594
|
+
showWaitingDialog(`Attempting to restore the backup from ${name}`, 180000);
|
|
3595
|
+
const start = Date.now();
|
|
3596
|
+
sendToWrapper(namespace, 'restore', {name}, function(msg) {
|
|
3597
|
+
closeWaitingDialog();
|
|
3598
|
+
if (msg.error) {
|
|
3599
|
+
showMessage(msg.error, _('Error'))
|
|
3600
|
+
}
|
|
3601
|
+
else {
|
|
3602
|
+
const duration = Date.now() - start;
|
|
3603
|
+
showMessage(`Restored configuration from backup after ${duration / 1000} s`, 'Restore successful');
|
|
3604
|
+
}
|
|
3605
|
+
})
|
|
3606
|
+
})
|
|
3607
|
+
})
|
|
3608
|
+
|
|
3609
|
+
}
|
|
3260
3610
|
function showViewConfig() {
|
|
3261
3611
|
$('#modalviewconfig').modal('open');
|
|
3262
3612
|
}
|
|
@@ -3643,14 +3993,13 @@ function genDevInfo(device) {
|
|
|
3643
3993
|
if (value === undefined) {
|
|
3644
3994
|
return '';
|
|
3645
3995
|
} else {
|
|
3646
|
-
|
|
3996
|
+
const label = `${name=='' ? ' ' : name + ':'}`;
|
|
3997
|
+
return `<li><span class="label">${label.replace('_',' ')}</span><span>${value}</span></li>`;
|
|
3647
3998
|
}
|
|
3648
3999
|
};
|
|
3649
4000
|
const genRowValues = function (name, value) {
|
|
3650
|
-
if (value
|
|
3651
|
-
|
|
3652
|
-
} else {
|
|
3653
|
-
let label = `${name}:`;
|
|
4001
|
+
if (Array.isArray(value)) {
|
|
4002
|
+
let label = `${name=='' ? ' ' : name + ':'}`;
|
|
3654
4003
|
try {
|
|
3655
4004
|
return value.map((val) => {
|
|
3656
4005
|
const row = `<li><span class="label">${label}</span><span>${val}</span></li>`;
|
|
@@ -3662,6 +4011,7 @@ function genDevInfo(device) {
|
|
|
3662
4011
|
return `<li><span class="label">${label}</span><span>${JSON.stringify(value)}</span></li>`
|
|
3663
4012
|
}
|
|
3664
4013
|
}
|
|
4014
|
+
else return '';
|
|
3665
4015
|
};
|
|
3666
4016
|
const modelUrl = (!mapped) ? '' : `<a href="https://www.zigbee2mqtt.io/devices/${sanitizeModelParameter(mapped.model)}.html" target="_blank" rel="noopener noreferrer">${mapped.model}</a>`;
|
|
3667
4017
|
const mappedInfo = [];
|
|
@@ -3673,7 +4023,7 @@ function genDevInfo(device) {
|
|
|
3673
4023
|
if (item == 'model')
|
|
3674
4024
|
mappedInfo.push(genRow(item,modelUrl));
|
|
3675
4025
|
else
|
|
3676
|
-
mappedInfo.push(genRow(item,mapped[item]));
|
|
4026
|
+
if (typeof mapped[item] != 'object') mappedInfo.push(genRow(item,mapped[item]));
|
|
3677
4027
|
}
|
|
3678
4028
|
mappedInfo.push(
|
|
3679
4029
|
` </ul>
|
|
@@ -3695,7 +4045,7 @@ function genDevInfo(device) {
|
|
|
3695
4045
|
const imgSrc = device.icon || device.common.icon;
|
|
3696
4046
|
const imgInfo = (imgSrc) ? `<img src=${imgSrc} width='150px' onerror="this.onerror=null;this.src='img/unavailable.png';"><div class="divider"></div>` : '';
|
|
3697
4047
|
const info =[
|
|
3698
|
-
`<div class="col s12 m6 l6 xl6">
|
|
4048
|
+
`<div class="col ${device.memberinfo != undefined ? 's12 m12 l12 xl12':'s12 m6 l6 xl6'}">
|
|
3699
4049
|
${imgInfo}
|
|
3700
4050
|
${mappedInfo.join('')}
|
|
3701
4051
|
<div class="divider"></div>
|
|
@@ -3704,8 +4054,19 @@ function genDevInfo(device) {
|
|
|
3704
4054
|
for (const item in dev) {
|
|
3705
4055
|
info.push(genRow(item, dev[item]));
|
|
3706
4056
|
}
|
|
3707
|
-
|
|
3708
|
-
|
|
4057
|
+
if (device.memberinfo != undefined) {
|
|
4058
|
+
const memberCount = (device.memberinfo.length);
|
|
4059
|
+
if (memberCount != 1) info.push(genRow(`Members`, `${memberCount}`));
|
|
4060
|
+
for (let m = 0; m < device.memberinfo.length; m++) {
|
|
4061
|
+
const dev = getDeviceByIEEE(device.memberinfo[m].ieee);
|
|
4062
|
+
const epid = device.memberinfo[m].epid;
|
|
4063
|
+
const epname = Array.isArray(dev.info.endpoints) ? `:${dev.info.endpoints.find((item) => item.ID == epid)?.epName}` : undefined;
|
|
4064
|
+
info.push(genRow(`Member${memberCount > 1 ? ' ' + (m+1) : ''}`, `${device.memberinfo[m].device}${epname ? epname : ''} - ${device.memberinfo[m].ieee}.${epid}`));
|
|
4065
|
+
}
|
|
4066
|
+
info.push(`</div>
|
|
4067
|
+
</div>`);
|
|
4068
|
+
}
|
|
4069
|
+
else info.push(`${genRow('configured', (device.isConfigured), true)}</ul>
|
|
3709
4070
|
</div>
|
|
3710
4071
|
</div>
|
|
3711
4072
|
<div class="col s12 m6 l6 xl6">
|
|
@@ -4064,7 +4425,8 @@ function removeDevice(id) {
|
|
|
4064
4425
|
}
|
|
4065
4426
|
|
|
4066
4427
|
function swapActive(id) {
|
|
4067
|
-
const dev = getDeviceByID(id);
|
|
4428
|
+
const dev = getDeviceByID(id) || getDeviceByIEEE(`0x${id}`);
|
|
4429
|
+
console.warn(`swap_active for ${id} -> ${JSON.stringify(dev)}`);
|
|
4068
4430
|
if (dev && dev.common) {
|
|
4069
4431
|
dev.common.deactivated = !(dev.common.deactivated);
|
|
4070
4432
|
sendToWrapper(namespace, 'setDeviceActivated', {id: id, deactivated: dev.common.deactivated}, function () {
|