iobroker.linux-control
Version:
Controlling Linux devices and get information about your system
574 lines (488 loc) • 20.7 kB
JavaScript
var hosts = [];
var folders = [];
var commands = [];
var serviceWhiteList = [];
var blacklistDatapoints = [];
var secret;
var _settings;
var myNamespace;
// This will be called by the admin adapter when the settings page loads
async function load(settings, onChange) {
// example: select elements with id=key and class=value and insert value
if (!settings) return;
myNamespace = `${adapter}.${instance}`;
addVersionToAdapterTitle();
hosts = settings.hosts || [];
folders = settings.folders || [];
commands = settings.commands || [];
serviceWhiteList = settings.serviceWhiteList || [];
blacklistDatapoints = settings.blacklistDatapoints || [];
// @ts-ignore
socket.emit('getObject', 'system.config', function (err, obj) {
secret = (obj.native ? obj.native.secret : '') || 'Zgfr56gFe87jJOM';
loadHostsTable(settings, hosts, onChange);
});
$('.value').each(function () {
var $key = $(this);
var id = $key.attr('id');
if ($key.attr('type') === 'checkbox') {
// do not call onChange direct, because onChange could expect some arguments
$key.prop('checked', settings[id])
.on('change', () => onChange())
;
} else {
// do not call onChange direct, because onChange could expect some arguments
$key.val(settings[id])
.on('change', () => onChange())
.on('keyup', () => onChange())
;
}
});
_settings = settings;
await createTreeViews(settings, onChange);
createFoldersTable(folders, hosts, onChange);
createCommandsTable(commands, hosts, onChange);
createServicesWhiteListChips(hosts, settings, onChange);
createDatapointsBlacklistChips(hosts, settings, onChange);
eventsHandler(settings, onChange);
onChange(false);
// reinitialize all the Materialize labels on the page if you are dynamically adding inputs:
if (M) M.updateTextFields();
}
function eventsHandler(settings, onChange) {
$('.foldersTab').on('click', function () {
//recreate Table on Tab click -> dynamically create select options
hosts = table2values('hosts');
folders = table2values('folders');
createFoldersTable(folders, hosts, onChange);
});
$('.mycommandsTab').on('click', function () {
//recreate Table on Tab click -> dynamically create select options
hosts = table2values('hosts');
commands = table2values('commands');
createCommandsTable(commands, hosts, onChange);
});
$('.servicesTab').on('click', function () {
//recreate Table on Tab click -> dynamically create select options
hosts = table2values('hosts');
for (const host of hosts) {
serviceWhiteList[host.name] = chips2list(`.whitelistServices_${host.name}`)
}
createServicesWhiteListChips(hosts, settings, onChange);
});
$('.datapointsTab').on('click', function () {
//recreate Table on Tab click -> dynamically create select options
hosts = table2values('hosts');
for (const host of hosts) {
blacklistDatapoints[host.name] = chips2list(`.blacklistDatapoints_${host.name}`)
}
createDatapointsBlacklistChips(hosts, settings, onChange);
});
}
async function createTreeViews(settings, onChange) {
let tree = []
for (const key of Object.keys(settings.whitelist)) {
let propList = await getObjects(key);
let children = [];
let expanded = false;
for (const child of propList) {
let selected = false;
if (settings.whitelist[key] && settings.whitelist[key].includes(child.id)) {
selected = true;
expanded = true;
}
children.push({
title: `<div class="fancytree-item-title-id">${child.id}</div><div class="fancytree-item-title-name">${_(child.name)}</div>`,
key: `${key}.${child.id}`,
selected: selected
})
}
tree.push({
title: `<div class="fancytree-folder-title-id">${key}</div><div class="fancytree-item-title-name">${_(`root_${key}`)}</div>`,
key: key,
folder: true,
expanded: expanded,
children: children
})
}
$(`#tree_datapoints`).fancytree({
extensions: ["dnd5"], // Drag & Drop lib
activeVisible: true, // Make sure, active nodes are visible (expanded)
aria: true, // Enable WAI-ARIA support
autoActivate: true, // Automatically activate a node when it is focused using keyboard
autoCollapse: false, // Automatically collapse all siblings, when a node is expanded
autoScroll: false, // Automatically scroll nodes into visible area
clickFolderMode: 2, // 1:activate, 2:expand, 3:activate and expand, 4:activate (dblclick expands)
checkbox: true, // Show check boxes
checkboxAutoHide: false, // Display check boxes on hover only
debugLevel: 0, // 0:quiet, 1:errors, 2:warnings, 3:infos, 4:debug
disabled: false, // Disable control
focusOnSelect: false, // Set focus when node is checked by a mouse click
escapeTitles: false, // Escape `node.title` content for display
generateIds: false, // Generate id attributes like <span id='fancytree-id-KEY'>
keyboard: true, // Support keyboard navigation
keyPathSeparator: "/", // Used by node.getKeyPath() and tree.loadKeyPath()
minExpandLevel: 1, // 1: root node is not collapsible
quicksearch: false, // Navigate to next node by typing the first letters
rtl: false, // Enable RTL (right-to-left) mode
selectMode: 3, // 1:single, 2:multi, 3:multi-hier
tabindex: "0", // Whole tree behaves as one single control
titlesTabbable: false, // Node titles can receive keyboard focus
tooltip: false, // Use title as tooltip (also a callback could be specified)
// icon: function (event, data) {
// if (data.node.isFolder()) {
// return "unifi.png";
// }
// },
source:
tree
,
click: function (event, data) {
if (data.targetType === 'title' && !data.node.folder) {
data.node.setSelected(!data.node.isSelected());
}
if (data.targetType === 'checkbox' && data.node.folder) {
data.node.setExpanded(!data.node.isSelected());
}
},
select: function (event, data) {
// Funktion um alle title auszulesen, kann für Übersetzung verwendet werden -> bitte drin lassen!
// var selKeys = $.map(data.tree.getSelectedNodes(), function (node) {
// if (node.children === null) {
// return node.title;
// }
// });
// console.log(selKeys.join('\n').replace(/_/g, " "));
onChange();
},
dnd5: {
// // Available options with their default:
// autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering.
// dropMarkerOffsetX: -24, // absolute position offset for .fancytree-drop-marker
// // relatively to ..fancytree-title (icon/img near a node accepting drop)
// dropMarkerInsertOffsetX: -16, // additional offset for drop-marker with hitMode = "before"/"after"
// effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations
// // (can also be set in the dragStart event)
// dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move')
// // when no modifier is pressed (overide in dragDrag, dragOver).
// multiSource: false, // true: Drag multiple (i.e. selected) nodes.
// // Also a callback() is allowed to return a node list
// preventForeignNodes: false, // Prevent dropping nodes from another Fancytree
// preventLazyParents: true, // Prevent dropping items on unloaded lazy Fancytree nodes
// preventNonNodes: false, // Prevent dropping items other than Fancytree nodes
// preventRecursion: true, // Prevent dropping nodes on own descendants when in move-mode
// preventSameParent: false, // Prevent dropping nodes under same direct parent
// preventVoidMoves: true, // Prevent moving nodes 'before self', etc.
scroll: true, // Enable auto-scrolling while dragging
scrollSensitivity: 80, // Active top/bottom margin in pixel
// scrollSpeed: 5, // Pixel per event
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
dragStart: function (node, data) {
// Return false to cancel dragging of node.
// if (node.isFolder()) { return false; }
// Image must exist in DOM
var $dragItem = $(`<div class="fancytree-drag-item-container">
<img class="fancytree-drag-item-image" src="./img/state.png" />
<div class="fancytree-drag-text">${data.node.key}</div>
</div>`).appendTo("body");
data.dataTransfer.setDragImage($dragItem[0], -10, -10);
// Prevent henerating the default echo
data.useDefaultImage = false;
if (node.isFolder()) {
// let list = [];
// for (const child of node.children) {
// list.push(child.key);
// }
data.dataTransfer.setData('text/plain', `${data.node.key}.all`);
} else {
data.dataTransfer.setData('text/plain', data.node.key);
}
return true;
}
}
});
}
function createServicesWhiteListChips(host, settings, onChange) {
$('.container_whitelistServices').empty();
for (const host of hosts) {
if (host && host.name) {
$('.container_whitelistServices').append(
`<div class="col s12 m12 l12">
<div class="col s12 m12 l12 card">
<div class="card-content blacklistDatapoints">
<div class="row card-title">
<div class="col s12 m9 l9">
<div class="translate">${host.name}: ${_('WhitelistServices')}</div>
</div>
</div>
<div class="chips whitelistServices_${host.name}"></div>
</div>
</div>
</div>`
)
list2chips(`.whitelistServices_${host.name}`, serviceWhiteList[host.name] || [], onChange, 'AddService');
}
}
}
function createDatapointsBlacklistChips(host, settings, onChange) {
$('.container_blacklistDatapoints').empty();
for (const host of hosts) {
if (host && host.name) {
$('.container_blacklistDatapoints').append(
`
<div class="col s12 m12 l12">
<div class="col s12 m12 l12 card">
<div class="card-content blacklistDatapoints">
<div class="row card-title">
<div class="col s12 m9 l9">
<div class="translate">${host.name}: ${_('blacklistDatapoints')}</div>
</div>
</div>
<div class="chips blacklistDatapoints_${host.name}"></div>
</div>
</div>
</div>`
)
list2chips(`.blacklistDatapoints_${host.name}`, blacklistDatapoints[host.name] || [], onChange, 'Drag here');
$(`.blacklistDatapoints_${host.name}`).find('input').prop('readonly', true);
$(`.blacklistDatapoints_${host.name}`).on('dragover', false).on('drop', function (e) {
// do something
let data = e.originalEvent.dataTransfer.getData("text/plain");
if (data.includes(',')) {
let list = data.split(',');
for (const child of list) {
M.Chips.getInstance($(`.blacklistDatapoints_${host.name}`)).addChip({ tag: child });
}
} else {
M.Chips.getInstance($(`.blacklistDatapoints_${host.name}`)).addChip({ tag: data });
}
return false;
});
}
}
}
function createCommandsTable(data, hosts, onChange) {
let hostNames = [];
for (const host of hosts) {
if (host && host.name) {
hostNames.push(host.name);
}
}
$('.container_mycommandsTable').empty();
let element = `<div class="col s12" id="commands">
<div>
<a class="btn-floating waves-effect waves-light blue table-button-add"><i
class="material-icons">add</i></a>
<span class="my-card-subTitle translate" style="margin-left: 10px;">${_('add_command')}</span>
</div>
<div class="table-values-div" style="margin-top: 10px;">
<table class="table-values" id="commandsTable">
<thead>
<tr>
<th data-name="enabled" data-type="checkbox" checked="true" data-default="true" style="width: 20px;" data-style="width: 20px;"
class="translate">${_("enabled")}</th>
<th data-name="host" class="selectInTable-hosts translate"
style="width: 10%; text-align: center;" data-default=""
data-type="select" data-style=""
data-options="${hostNames.join(";")}">${_("Host")}</th>
<th data-name="name" style="width: 10%" class="translate">${_("Name")}</th>
<th data-name="interval" data-type="number" data-default="0"
style="width: 5%; text-align: center;" class="translate">${_("pollingInterval_seconds")}</th>
<th data-name="description" style="width: auto" class="translate">${_("Description")}</th>
<th data-name="command" style="width: 27%" class="translate">${_("Command")}</th>
<th data-name="type" class="translate"
style="width: 10%; text-align: center;" data-default="string"
data-type="select" data-style=""
data-options="string;number;boolean;button;array">${_("Type")}</th>
<th data-name="unit" style="width: 100px; text-align: center;" class="translate">${_("Unit")}</th>
<th data-buttons="delete up down" style="width: 120px"></th>
</tr>
</thead>
</table>
</div>
</div>`
$('.container_mycommandsTable').html(element);
values2table('commands', data, onChange);
}
function createFoldersTable(data, hosts, onChange) {
let hostNames = [];
for (const host of hosts) {
if (host && host.name) {
hostNames.push(host.name);
}
}
$('.container_foldersTable').empty();
let element = `<div class="col s12" id="folders">
<div>
<a class="btn-floating waves-effect waves-light blue table-button-add"><i
class="material-icons">add</i></a>
<span class="my-card-subTitle translate" style="margin-left: 10px;">${_('add_folder')}</span>
</div>
<div class="table-values-div" style="margin-top: 10px;">
<table class="table-values" id="foldersTable">
<thead>
<tr>
<th data-name="enabled" data-type="checkbox" checked="true" data-default="true" style="width: 20px;" data-style="width: 20px;"
class="translate">${_("enabled")}</th>
<th data-name="host" class="selectInTable-hosts translate"
style="width: 10%; text-align: center;" data-default=""
data-type="select" data-style=""
data-options="${hostNames.join(";")}">${_("Host")}</th>
<th data-name="name" style="width: 15%;" class="translate">${_("Name")}</th>
<th data-name="path" style="width: auto" class="translate">${_("Path")}</th>
<th data-name="fileNamePattern" style="width: 10%" class="translate">${_("fileNamePattern")}</th>
<th data-name="unit" class="translate"
style="width: 100px; text-align: center;" data-default="MB"
data-type="select" data-style=""
data-options="MB;GB;TB">${_("Unit")}</th>
<th data-name="digits" data-type="number" data-default="2" style="width: 100px" class="translate">${_("Digits")}</th>
<th data-name="countFiles" data-type="checkbox" checked="true" data-default="true" style="width: 20px; text-align: center;" data-style="width: 20px;"
class="translate">${_("countFiles")}</th>
<th data-name="lastChange" data-type="checkbox" checked="true" data-default="true" style="width: 20px; text-align: center;" data-style="width: 20px;"
class="translate">${_("lastChange")}</th>
<th data-buttons="delete up down" style="width: 120px"></th>
</tr>
</thead>
</table>
</div>
</div>`
$('.container_foldersTable').html(element);
values2table('folders', data, onChange);
}
async function getObjects(lib) {
return new Promise((resolve, reject) => {
$.getJSON(`./lib/${lib}.json`, function (json) {
if (json) {
resolve(json);
} else {
resolve(null);
}
});
});
}
async function getObjectAsync(id) {
return new Promise((resolve, reject) => {
socket.emit('getObject', id, function (err, res) {
if (!err && res) {
resolve(res);
} else {
resolve(null);
}
});
});
}
function loadHostsTable(settings, hosts, onChange) {
if (hosts.length > 0) {
for (const host of hosts) {
host.password = decrypt(secret, host.password);
}
}
values2table('hosts', hosts, onChange);
onChange(false);
// function Materialize.updateTextFields(); to reinitialize all the Materialize labels on the page if you are dynamically adding inputs.
M.updateTextFields();
}
// This will be called by the admin adapter when the user presses the save button
function save(callback) {
// example: select elements with class=value and build settings object
var obj = {};
$('.value').each(function () {
var $this = $(this);
if ($this.attr('type') === 'checkbox') {
obj[$this.attr('id')] = $this.prop('checked');
} else {
obj[$this.attr('id')] = $this.val();
}
});
obj.serviceWhiteList = {};
obj.blacklistDatapoints = {};
obj.hosts = table2values('hosts').filter(o => (o.name !== ''));
if (obj.hosts.length > 0) {
for (const host of obj.hosts) {
host.password = encrypt(secret, host.password);
host.name = host.name.replace(/[*?"'\[\]\s]/g, "_");
obj.serviceWhiteList[host.name] = chips2list(`.whitelistServices_${host.name}`)
obj.blacklistDatapoints[host.name] = chips2list(`.blacklistDatapoints_${host.name}`)
}
}
obj.folders = table2values('folders').filter(o => (o.name !== ''));
if (obj.folders.length > 0) {
for (const host of obj.folders) {
host.name = host.name.replace(/[*?"'\[\]\s]/g, "_");
}
}
obj.commands = table2values('commands').filter(o => (o.name !== ''));
if (obj.commands.length > 0) {
for (const host of obj.commands) {
host.name = host.name.replace(/[*?"'\[\]\s]/g, "_");
}
}
obj.whitelist = {}
for (const key of Object.keys(_settings.whitelist)) {
obj.whitelist[key] = [];
}
$("[id*=tree_]").each(function () {
var selected = $.ui.fancytree.getTree(`#tree_datapoints`).getSelectedNodes();
var selectedIds = $.map(selected, function (node) {
if (!node.folder) {
return node.key;
}
});
for (const id of selectedIds) {
let idSplitted = id.split('.');
obj.whitelist[idSplitted[0]].push(idSplitted[1]);
}
});
callback(obj);
}
function encrypt(key, value) {
var result = '';
for (var i = 0; i < value.length; ++i) {
result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
}
return result;
}
function decrypt(key, value) {
var result = '';
for (var i = 0; i < value.length; ++i) {
result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
}
return result;
}
/**
* Chips
*/
function list2chips(selector, list, onChange, placeholder) {
const data = [];
list.sort();
for (let i = 0; i < list.length; i++) {
if (list[i] && list[i].trim()) {
data.push({ tag: list[i].trim() });
}
}
$(selector).chips({
data: data,
placeholder: _(placeholder),
secondaryPlaceholder: _(placeholder),
onChipAdd: onChange,
onChipDelete: onChange
});
}
function chips2list(selector) {
const list = [];
if ($(selector).length > 0) {
const data = $(selector).chips('getData');
for (let i = 0; i < data.length; i++) {
list.push(data[i].tag);
}
list.sort();
}
return list;
}
async function addVersionToAdapterTitle() {
let instanceObj = await getObjectAsync(`system.adapter.${myNamespace}`);
if (instanceObj && instanceObj.common && instanceObj.common.installedVersion) {
let title = $('#adapterTitle');
title.html(`${title.html()} <font size="3"><i>${instanceObj.common.installedVersion}</i></font>`);
}
}