/************************************************************************************************************************************************************************
Version: 1.0.2
created by Scrounger
Dieses Skript erzeugt json strings um Proxmox Informationen im VIS mit den Material Design Widgets darzustellen
!!! Voraussetzungen !!!
- Material Design Widgets >= 0.3.19
- Proxmox >= 1.0.2
- Javascript Adapter >= 4.6.1
- Javascript Adapter NPM Module: moment, moment-timezone, moment-duration-format, mathjs
=========================================================================================================================================================================
--- Links ---
=========================================================================================================================================================================
--- Changelog ---
- 1.0.0: Initial release
- 1.0.1: Number decimal format changed
- 1.0.2: Bug Fix wenn nur ein Datenpunkt für die Temperatur verwendet wird
- 1.0.3: Einstellung 'iconColor' für icon Farbe hinzugefügt
************************************************************************************************************************************************************************/
// Skript Einstellungen *************************************************************************************************************************************************
let idDatenpunktPrefix = '0_userdata.0' // '0_userdata.0' or 'javascript.x'
let idDatenPunktStrukturPrefix = 'Vis.MaterialDesignWidgets.Proxmox' // Struktur unter Prefix
let triggerDatenpunkt = "proxmox.0.node_pve.uptime"; // Datenpunkt um Skript Ausführung zu triggern (z.B. uptime einer Node)
let cpuAverageLastValues = 60; // Wieviele Werte zur Berechnung der durchschnittlichen CPU Last verwendet werden sollen
let nodesList = [ // Node Liste
{
idChannel: 'proxmox.0.node_pve', // id des Channels der Node
targetChannel: 'promox', // id unter der der json string für das Table Widget gespeichert werden soll
name: 'System', // name der als Titel angezeigt werden soll
image: '/vis.0/Bilder/GeraeteBatterien/NUC.png', // Bild das im Titel angezeigt werden soll
url: 'https://192.168.178.210:8006/', // Url die aufgerufen wird beim Klick auf den Titel
showControlButtons: true, // Buttons für start, restart, stop anzeigen
// temperatures: ['linkeddevices.0.System.Temperatur.Core_0', 'linkeddevices.0.System.Temperatur.Core_1'], // Datenpunkte für Temperatur (1 oder 2 Datenpunkte, entfernen wenn nicht benötigt)
storages: [ // Storage Datenpunkt des Proxmox Adapter anzeigen
{
idChannel: 'proxmox.0.storage.pve_local', // id des Storage Datenpunkts
text: 'Local', // Text der für den Storage angezeigt werden soll
icon: 'harddisk' // Icon das für den Storage angezeigt werden soll
},
{
idChannel: 'proxmox.0.storage.pve_local-lvm',
text: 'LVM',
icon: 'harddisk'
},
{
idChannel: 'proxmox.0.storage.pve_local2_60gb',
text: 'Backup',
icon: 'harddisk'
},
{
idChannel: 'proxmox.0.storage.pve_Backups',
text: 'Mirror',
icon: 'harddisk'
}
],
custom: [ // andere Datenpunkte (nicht vom Proxmox Adapter) die mit aufgelistet werden sollen. Falls nicht benötigt, Array löschen
{
id: 'linux-control.0.proxmox.needrestart.needrestart', // id des Datenpunktes
text: 'Neustart notwendig', // text der angezeigt werden soll
icon: 'restart', // icon das angezeigt werden soll
type: 'boolean', // welche Funktion verwendet werden soll
attention: true // ob Attention Farbe angezeigt werden soll
},
{
id: 'linux-control.0.proxmox.updates.newPackages', // id des Datenpunktes
text: 'Updates', // text der angezeigt werden soll
icon: 'package-down', // icon das angezeigt werden soll
type: 'number', // welche Funktion verwendet werden soll
attention: true // ob Attention Farbe angezeigt werden soll
},
{
id: 'linux-control.0.proxmox.updates.lastUpdate', // id des Datenpunktes
text: 'letztes Update', // text der angezeigt werden soll
icon: 'package-up', // icon das angezeigt werden soll
type: 'timestamp', // welche Funktion verwendet werden soll
}
]
}
]
let vmList = [ // LXC / VM Liste
{
idChannel: 'proxmox.0.lxc.iobroker', // id des Channels der Node
targetChannel: 'lxc_ioBroker', // id unter der der json string für das Table Widget gespeichert werden soll
name: 'LXC - ioBroker', // name der als Titel angezeigt werden soll
image: '/vis.0/Bilder/Index/admin.png', // Bild das im Titel angezeigt werden soll
url: 'http://192.168.178.213:8081', // Url die aufgerufen wird beim Klick auf den Titel
custom: [ // andere Datenpunkte (nicht vom Proxmox Adapter) die mit aufgelistet werden sollen. Falls nicht benötigt, Array löschen
{
id: 'linux-control.0.iobroker.needrestart.needrestart', // id des Datenpunktes
text: 'Neustart notwendig', // text der angezeigt werden soll
icon: 'restart', // icon das angezeigt werden soll
type: 'boolean', // welche Funktion verwendet werden soll
attention: true // ob Attention Farbe angezeigt werden soll
},
{
id: 'linux-control.0.iobroker.updates.newPackages', // id des Datenpunktes
text: 'Updates', // text der angezeigt werden soll
icon: 'package-down', // icon das angezeigt werden soll
type: 'number', // welche Funktion verwendet werden soll
attention: true // ob Attention Farbe angezeigt werden soll
},
{
id: 'linux-control.0.iobroker.updates.lastUpdate', // id des Datenpunktes
text: 'letztes Update', // text der angezeigt werden soll
icon: 'package-up', // icon das angezeigt werden soll
type: 'timestamp', // welche Funktion verwendet werden soll
},
// {
// id: 'proxmox.0.lxc_ioBroker.folders.backup.container.lastChange', // id des Datenpunktes
// secondIds: [ // ids für subtext
// 'proxmox.0.lxc_ioBroker.folders.backup.container.files',
// 'proxmox.0.lxc_ioBroker.folders.backup.container.size'
// ],
// text: 'LXC Backup', // text der angezeigt werden soll
// icon: 'backup-restore', // icon das angezeigt werden soll
// type: 'timestamp'
// },
// {
// id: 'proxmox.0.lxc_ioBroker.folders.backup.data.lastChange', // id des Datenpunktes
// secondIds: [ // ids für subtext
// 'proxmox.0.lxc_ioBroker.folders.backup.data.files',
// 'proxmox.0.lxc_ioBroker.folders.backup.data.size'
// ],
// text: 'Daten Backup', // text der angezeigt werden soll
// icon: 'file-upload', // icon das angezeigt werden soll
// type: 'timestamp'
// },
// {
// id: 'proxmox.0.lxc_ioBroker.folders.ioBroker.size', // id des Datenpunktes
// text: 'Ordnergröße', // text der angezeigt werden soll
// icon: 'folder-information', // icon das angezeigt werden soll
// },
// {
// id: 'proxmox.0.lxc_ioBroker.folders.npm_cache.size', // id des Datenpunktes
// text: 'NPM Cache', // text der angezeigt werden soll
// icon: 'folder-clock', // icon das angezeigt werden soll
// }
]
},
/{
idChannel: 'proxmox.0.lxc.Proxy', // id des Channels der Node
targetChannel: 'lxc_Proxy', // id unter der der json string für das Table Widget gespeichert werden soll
name: 'LXC - proxy', // name der als Titel angezeigt werden soll
// image: '/vis.0/myImages/nextcloud-icon.png', // Bild das im Titel angezeigt werden soll
url: 'https://192.168.178.215', // Url die aufgerufen wird beim Klick auf den Titel
// custom: [ // andere Datenpunkte (nicht vom Proxmox Adapter) die mit aufgelistet werden sollen. Falls nicht benötigt, Array löschen
// {
// id: 'linux-control.0.lxc_NextCloud.needrestart.needrestart', // id des Datenpunktes
// text: 'Neustart notwendig', // text der angezeigt werden soll
// icon: 'restart', // icon das angezeigt werden soll
// type: 'boolean', // welche Funktion verwendet werden soll
// attention: true // ob Attention Farbe angezeigt werden soll
// },
// {
// id: 'linux-control.0.lxc_NextCloud.updates.newPackages', // id des Datenpunktes
// text: 'Updates', // text der angezeigt werden soll
// icon: 'package-down', // icon das angezeigt werden soll
// type: 'number', // welche Funktion verwendet werden soll
// attention: true // ob Attention Farbe angezeigt werden soll
// },
// {
// id: 'linux-control.0.lxc_NextCloud.updates.lastUpdate', // id des Datenpunktes
// text: 'letztes Update', // text der angezeigt werden soll
// icon: 'package-up', // icon das angezeigt werden soll
// type: 'timestamp', // welche Funktion verwendet werden soll
// },
// {
// id: 'linux-control.0.lxc_NextCloud.folders.backup.container.lastChange', // id des Datenpunktes
// secondIds: [ // ids für subtext
// 'linux-control.0.lxc_NextCloud.folders.backup.container.files',
// 'linux-control.0.lxc_NextCloud.folders.backup.container.size'
// ],
// text: 'LXC Backup', // text der angezeigt werden soll
// icon: 'backup-restore', // icon das angezeigt werden soll
// type: 'timestamp'
// },
// {
// id: 'linux-control.0.lxc_NextCloud.folders.userData.size', // id des Datenpunktes
// text: 'Benutzerdaten', // text der angezeigt werden soll
// icon: 'folder-account', // icon das angezeigt werden soll
// }
// ]
},/
{
idChannel: 'proxmox.0.qemu.RaspberryMatic', // id des Channels der Node
targetChannel: 'qemu_RaspiMatic', // id unter der der json string für das Table Widget gespeichert werden soll
name: 'VM - RaspiMatic', // name der als Titel angezeigt werden soll
image: '/vis.0/Bilder/GeraeteBatterien/raspberrymatic.png', // Bild das im Titel angezeigt werden soll
url: 'http://192.168.178.212/login.htm', // Url die aufgerufen wird beim Klick auf den Titel
},
]
let fontSizePrimary = 20;
let fontSizeSecondary = 16;
let fontSizeTertiary = 14;
let fontSizeQuinary = 11;
let fontFamilyPrimary = 'Roboto,sans-serif';
let fontFamilySecondary = 'RobotoCondensed-Regular';
let fontFamilyTertiary = 'RobotoCondensed-Light';
let fontFamilyQuaternary = 'RobotoCondensed-LightItalic';
let colorPrimary = '#44739e';
let colorSecondary = 'gray';
let colorTertiary = '#44739e';
let colorOnline = 'green';
let colorOffline = 'FireBrick';
let colorGood = 'green';
let colorMedium = 'gold';
let colorBad = 'FireBrick';
let iconColor = '#44739e'
let iconAttentionColor = '#f27935';
let colCount = 24; // Anzahl der Spalten die im Widget eingestellt sind (+1 weil 0 im VIS Editor mitzählt)
let colSpanIcon = 3; // Anzahl der Spalten die für das icon verwendet werden soll
let colSpanText = 8; // Anzahl der Spalten die für den Text verwendet werden soll
let colSpanValueText = colCount - colSpanIcon - colSpanText;
let rowHeight = 32;
let styleValue = font-size: ${fontSizeTertiary}px; text-align: right; margin-right: 8px; font-family: ${fontFamilyTertiary}; color: ${colorTertiary};
let styleText = font-size: ${fontSizeSecondary}px; text-align: left; font-family: ${fontFamilySecondary}; color: ${colorPrimary}; height: ${rowHeight}px; line-height: ${rowHeight}px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;
let styleButtonText = font-size: ${fontSizeSecondary}px; text-align: left; font-family: ${fontFamilyTertiary}; color: ${colorPrimary}; margin-left: 2px; margin-right: 2px;
let iconLayout = {
type: "materialdesignicon",
mdwIconSize: 26,
colspan: colSpanIcon,
cellStyleAttrs: 'text-overflow: unset'
}
let textLayout = {
type: "html",
width: "100%",
cellStyleAttrs: 'padding-left: 2px;',
colspan: colSpanText
}
let valueTextLayout = {
type: "html",
width: "100%",
colspan: colSpanValueText,
}
let progressBarLayout = {
type: "progress",
width: "100%",
height: ${rowHeight}px
,
showValueLabel: true,
textAlign: "end",
colorProgress: colorGood,
colorOneCondition: 69,
colorOne: colorMedium,
colorTwoCondition: 89,
colorTwo: colorBad,
progressRounded: false,
verticalAlign: 'top',
textFontSize: fontSizeTertiary,
textFontFamily: fontFamilyTertiary,
colspan: colSpanValueText
}
let progressBarCpuLayout = {
type: "progress",
width: "100%",
height: ${rowHeight / 2}px
,
showValueLabel: true,
textAlign: "end",
colorProgress: colorGood,
colorOneCondition: 69,
colorOne: colorMedium,
colorTwoCondition: 89,
colorTwo: colorBad,
progressRounded: false,
textFontSize: fontSizeTertiary,
textFontFamily: fontFamilyTertiary,
colspan: colSpanValueText
}
let temperatureMaxValue = 90;
let progressBarTemperaturLayout = {
type: "progress",
width: "100%",
min: 0,
max: temperatureMaxValue,
showValueLabel: true,
textAlign: "end",
colorProgress: colorGood,
colorOneCondition: 59 / temperatureMaxValue * 100,
colorOne: colorMedium,
colorTwoCondition: 69 / temperatureMaxValue * 100,
colorTwo: colorBad,
progressRounded: false,
textFontSize: fontSizeTertiary,
textFontFamily: fontFamilyTertiary,
colspan: colSpanValueText,
valueLabelStyle: 'progressCustom'
}
let buttonControlLayout = {
type: "buttonState",
width: "100%",
height: "40px",
buttonStyle: "text",
vibrateOnMobilDevices: 50,
iconPosition: "left",
iconHeight: "20",
labelWidth: "",
autoLockAfter: 5,
lockEnabled: true,
lockIconColor: "FireBrick",
}
let statusSeperator = {
type: "html",
width: "100%",
colspan: colCount
}
let iconButtonControlLayout = {
type: "buttonState_icon",
width: ${rowHeight}px
,
height: ${rowHeight}px
,
imageColor: colorPrimary,
vibrateOnMobilDevices: "50",
autoLockAfter: "5",
lockIconTop: "32",
lockIconLeft: "30",
lockIconSize: "12",
lockIconColor: "red",
lockFilterGrayscale: "30",
image: "update",
iconHeight: "26",
lockEnabled: true,
lockIconBackground: "white",
lockBackgroundSizeFactor: "1.1"
}
// **********************************************************************************************************************************************************************
//import
const mathjs = require("mathjs");
const moment = require("moment");
const momentDurationFormatSetup = require("moment-duration-format");
moment.locale("de");
// Trigger
on({ id: triggerDatenpunkt, change: 'any' }, updateData);
function updateData() {
for (const node of nodesList) {
updateVm(node, true);
}
for (const vm of vmList) {
updateVm(vm);
}
}
function updateVm(vm, isNode = false) {
try {
let table = [];
if (existsObject(`${vm.idChannel}`)) {
let channel = getObject(`${vm.idChannel}`)
if (channel && channel.common && channel.common.name) {
let row = {};
if (vm.url) {
row.button = {
type: "buttonLink",
href: vm.url,
openNewWindow: true,
width: "100%",
height: "46px",
buttonStyle: "text",
vibrateOnMobilDevices: "50",
iconPosition: "right",
image: vm.image,
iconHeight: "40",
labelWidth: "100",
buttontext: `<div style="font-family: ${fontFamilyPrimary}; font-size: ${fontSizePrimary}px; font-weight: 500; letter-spacing: .0125em; text-decoration: inherit; text-align: left;">${vm.name}</div>`,
colspan: colCount,
}
} else {
row.title = {
type: "html",
width: "100%",
height: "46px",
html: `<div style="display: flex; padding: 0 8px 0 8px; align-items: center;">
<div style="flex: 1; font-family: ${fontFamilyPrimary}; font-size: ${fontSizePrimary}px; color: ${colorPrimary}; font-weight: 500; letter-spacing: .0125em; text-decoration: inherit; text-align: left;">${vm.name}</div>
<img class="materialdesign-icon-image" src="${vm.image}" style="width: auto; height: 40px; ;">
</div>`,
colspan: colCount,
cellStyleAttrs: 'height: 49px;'
}
}
table.push(row)
}
table.push({
seperator: {
type: "html",
width: "100%",
cellStyleAttrs: 'top: -3px; position: relative;',
html: `<hr style="color: ${colorPrimary}; background-color: ${colorPrimary}; border-width: 0; height: 2px; margin-top: 0; margin-bottom: 0;">`,
colspan: colCount
}
})
} else {
logDpNotExist(vm.targetChannel, `${vm.idChannel}`);
}
generateUptimeRow(`${vm.idChannel}.uptime`, table, vm)
if (vm.custom && vm.custom.length > 0) {
for (const dp of vm.custom) {
generateCustomRow(dp, table, vm);
}
}
generateProgressBarCpuRow(`${vm.idChannel}.cpu`, table, vm, isNode);
generateProgressBarTemperatures(vm.temperatures, table, vm);
if (!isNode) {
generateProgressBarRow(`${vm.idChannel}.mem_lev`, table, vm, "memory", 'Arbeitsspeicher', getUsedOfText(`${vm.idChannel}.mem`, `${vm.idChannel}.maxmem`, vm.targetChannel));
generateProgressBarRow(`${vm.idChannel}.disk_lev`, table, vm, "harddisk", 'Local', getUsedOfText(`${vm.idChannel}.disk`, `${vm.idChannel}.maxdisk`, vm.targetChannel));
} else {
generateProgressBarRow(`${vm.idChannel}.memory.used_lev`, table, vm, "memory", 'Arbeitsspeicher', getUsedOfText(`${vm.idChannel}.memory.used`, `${vm.idChannel}.memory.total`, vm.targetChannel));
generateProgressBarRow(`${vm.idChannel}.swap.used_lev`, table, vm, "folder-swap", 'SWAP', getUsedOfText(`${vm.idChannel}.swap.used`, `${vm.idChannel}.swap.total`, vm.targetChannel));
if (vm.storages) {
for (const storage of vm.storages) {
generateProgressBarRow(`${storage.idChannel}.used_lev`, table, vm, storage.icon, storage.text, getUsedOfText(`${storage.idChannel}.used`, `${storage.idChannel}.total`, vm.targetChannel));
}
}
}
// generateStatusBar(`${vm.idChannel}.status`, table, vm, 'top: 3px; position: relative;');
table.push({
seperator: Object.assign({
html: `<hr style="background: transparent; border-width: 0; height: 1px;margin-top: 0; margin-bottom: 0;">`,
}, statusSeperator)
})
if (vm.showControlButtons || vm.showControlButtons === undefined) {
let btnIds = [];
let btnRow = {};
if (existsObject(`${vm.idChannel}.start`)) {
btnIds.push({ id: `${vm.idChannel}.start`, text: 'start' });
}
if (existsObject(`${vm.idChannel}.reboot`)) {
btnIds.push({ id: `${vm.idChannel}.reboot`, text: 'neustart' });
}
if (existsObject(`${vm.idChannel}.shutdown`)) {
btnIds.push({ id: `${vm.idChannel}.shutdown`, text: 'stop' });
}
for (var i = 0; i <= btnIds.length - 1; i++) {
let id = btnIds[i].id;
btnRow[`btn${i}`] = Object.assign(
{
oid: id,
value: true,
buttontext: `<div style="${styleButtonText}">${btnIds[i].text}</div>`,
image: "play-circle-outline",
colspan: colCount / btnIds.length
}, buttonControlLayout);
}
table.push(btnRow);
}
// generateStatusBar(`${vm.idChannel}.status`, table, vm, 'top: -3px; position: relative;');
mySetState(`${idDatenpunktPrefix}.${idDatenPunktStrukturPrefix}.${isNode ? 'node' : 'vm'}.${vm.targetChannel}.jsonTable`, JSON.stringify(table), 'string', 'JSON string für Tabellen Widget');
} catch (ex) {
console.error(`[updateVm - ${vm.targetChannel}] error: ${ex.message}, stack: ${ex.stack}`);
}
}
function generateUptimeRow(id, table, vm) {
let row = {};
row.icon = Object.assign({ mdwIcon: "clock-check-outline", mdwIconColor: iconColor }, iconLayout)
row.text = Object.assign({ html: `<div style="${styleText}">Betriebszeit</div>` }, textLayout);
if (existsState(id)) {
let duration = moment.duration(getState(id).val * 1000);
let durationText = duration.format('D [Tage] h [Std. und] m [Min.]');
if (duration.asDays() <= 2) {
durationText = duration.format('D [Tag] h [Std. und] m [Min.]');
}
row.value = Object.assign({ html: `<div style="${styleValue}">${durationText}</div>` }, valueTextLayout);
} else {
logDpNotExist(vm.targetChannel, id);
row.value = Object.assign({ html: `<div style="${styleValue}; color: red;">N/A</div>` }, valueTextLayout);
}
table.push(row);
}
function generateCustomRow(dp, table, vm) {
let row = {};
row.icon = Object.assign({ mdwIcon: dp.icon, mdwIconColor: iconColor }, iconLayout);
if (!dp.secondIds) {
row.text = Object.assign({ html: `<div style="${styleText}">${dp.text}</div>` }, textLayout);
} else {
let secondText = [];
for (const id of dp.secondIds) {
if (existsState(id)) {
let obj = getObject(id);
let unit = '';
if (obj && obj.common && obj.common.unit) {
unit = ' ' + obj.common.unit;
}
secondText.push(getState(id).val + unit);
} else {
logDpNotExist(vm.targetChannel, id);
secondText.push('N/A');
}
}
row.text = Object.assign({ html: getHtmlTwoLines(dp.text, secondText.join(', ')) }, textLayout);
}
if (existsState(dp.id)) {
let val = getState(dp.id).val;
let obj = getObject(dp.id);
let unit = '';
if (obj && obj.common && obj.common.unit) {
unit = obj.common.unit
}
if (!dp.type) {
if (obj.common && obj.common.type === 'number') {
row.value = Object.assign({ html: `<div style="${styleValue}">${formatValue(val, undefined, '.,')} ${unit}</div>` }, valueTextLayout);
} else {
row.value = Object.assign({ html: `<div style="${styleValue}">${val} ${unit}</div>` }, valueTextLayout);
}
} else if (dp.type === 'timestamp') {
row.value = Object.assign({ html: `<div style="${styleValue}">${getFormattedTimeStamp(val)}</div>` }, valueTextLayout);
} else if (dp.type === 'timestampInSeconds') {
row.value = Object.assign({ html: `<div style="${styleValue}">${getFormattedTimeStamp(val * 1000)}</div>` }, valueTextLayout);
} else if (dp.type === 'boolean') {
row.value = Object.assign({ html: `<div style="${styleValue}">${val ? 'ja' : 'nein'}</div>` }, valueTextLayout);
if (dp.attention && val) {
row.icon = Object.assign({ mdwIcon: dp.icon, mdwIconColor: iconAttentionColor }, iconLayout);
}
} else if (dp.type === 'number') {
row.value = Object.assign({ html: `<div style="${styleValue}">${val > 0 ? `${val} ${unit}` : 'nein'}</div>` }, valueTextLayout);
if (dp.attention && val > 0) {
row.icon = Object.assign({ mdwIcon: dp.icon, mdwIconColor: iconAttentionColor }, iconLayout);
}
}
} else {
logDpNotExist(vm.targetChannel, dp.id);
row.value = Object.assign({ html: `<div style="${styleValue}; color: red;">N/A</div>` }, valueTextLayout);
}
table.push(row);
}
function generateProgressBarTemperatures(idList, table, vm) {
if (idList && idList.length > 0) {
let row = {};
row.icon = Object.assign({ mdwIcon: "thermometer", rowspan: idList.length, mdwIconColor: iconColor }, iconLayout);
row.text = Object.assign({ html: `<div style="${styleText}">Temperatur</div>`, rowspan: idList.length }, textLayout);
if (idList[0] && existsState(idList[0])) {
row.progressBar = Object.assign({ oid: idList[0], valueLabelCustom: '[#value] °C', textColor: colorTertiary, verticalAlign: 'bottom', height: `${rowHeight / idList.length}px`, cellStyleAttrs: `line-height: ${rowHeight / idList.length}px; padding-bottom: 0;` }, progressBarTemperaturLayout);
} else {
logDpNotExist(vm.targetChannel, idList[0]);
row.progressBar = Object.assign({ valueLabelCustom: 'N/A', textColor: colorTertiary, verticalAlign: 'bottom', height: `${rowHeight / idList.length}px`, cellStyleAttrs: `line-height: ${rowHeight / idList.length}px; padding-bottom: 0;` }, progressBarTemperaturLayout);
}
table.push(row);
if (idList.length === 2) {
if (idList[1] && existsState(idList[1])) {
table.push({ temp: Object.assign({ oid: idList[1], valueLabelCustom: '[#value] °C', textColor: colorTertiary, verticalAlign: 'bottom', height: `${rowHeight / idList.length}px`, cellStyleAttrs: `line-height: ${rowHeight / idList.length}px; padding-bottom: 0;` }, progressBarTemperaturLayout) });
} else {
logDpNotExist(vm.targetChannel, idList[1]);
table.push({ temp: Object.assign({ valueLabelCustom: 'N/A', textColor: colorTertiary, verticalAlign: 'bottom', height: `${rowHeight / idList.length}px`, cellStyleAttrs: `line-height: ${rowHeight / idList.length}px; padding-bottom: 0;` }, progressBarTemperaturLayout) });
}
}
}
}
function generateProgressBarCpuRow(id, table, vm, isNode = false) {
let row = {};
row.icon = Object.assign({ mdwIcon: "cpu-64-bit", rowspan: 2, mdwIconColor: iconColor }, iconLayout);
row.text = Object.assign({ html: <div style="${styleText}">CPU</div>
, rowspan: 2 }, textLayout);
if (existsState(id)) {
calculateCpuAverage(vm.targetChannel, getState(id).val, isNode);
let cpuAverageId = `${idDatenpunktPrefix}.${idDatenPunktStrukturPrefix}.${isNode ? 'node' : 'vm'}.${vm.targetChannel}.cpuAverage`
row.progressBar = Object.assign({ oid: id, textColor: colorTertiary, verticalAlign: 'bottom', cellStyleAttrs: `line-height: ${rowHeight / 2}px; padding-bottom: 0;` }, progressBarCpuLayout);
table.push(row);
if (existsState(cpuAverageId)) {
table.push({ cpu: Object.assign({ oid: cpuAverageId, valueLabelStyle: 'progressCustom', valueLabelCustom: 'Ø [#value] %', textColor: colorTertiary, verticalAlign: 'top', cellStyleAttrs: `line-height: ${rowHeight / 2}px; padding-top: 0;` }, progressBarCpuLayout) });
} else {
logDpNotExist(vm.targetChannel, cpuAverageId);
table.push({ cpu: Object.assign({ valueLabelStyle: 'progressCustom', valueLabelCustom: 'N/A', textColor: 'red', verticalAlign: 'top', cellStyleAttrs: `line-height: ${rowHeight / 2}px; padding-top: 0;` }, progressBarCpuLayout) });
}
} else {
logDpNotExist(vm.targetChannel, id);
row.progressBar = Object.assign({ valueLabelStyle: 'progressCustom', valueLabelCustom: 'N/A', textColor: 'red', verticalAlign: 'bottom', cellStyleAttrs: `line-height: ${rowHeight / 2}px; padding-bottom: 0;` }, progressBarCpuLayout);
table.push(row);
table.push({ cpu: Object.assign({ valueLabelStyle: 'progressCustom', valueLabelCustom: 'N/A', textColor: 'red', verticalAlign: 'top', cellStyleAttrs: `line-height: ${rowHeight / 2}px; padding-top: 0;` }, progressBarCpuLayout) });
}
}
function generateProgressBarRow(id, table, vm, icon, textOne, textTwo) {
let row = {};
row.icon = Object.assign({ mdwIcon: icon, mdwIconColor: iconColor }, iconLayout);
if (existsState(id)) {
row.text = Object.assign({ html: getHtmlTwoLines(textOne, textTwo) }, textLayout);
row.progressBar = Object.assign({ oid: id, textColor: colorTertiary }, progressBarLayout);
} else {
logDpNotExist(vm.targetChannel, id);
row.text = Object.assign({ html: `<div style="${styleText}">${textOne}</div>` }, textLayout);
row.progressBar = Object.assign({ valueLabelStyle: 'progressCustom', valueLabelCustom: 'N/A', textColor: 'red', }, progressBarLayout);
}
table.push(row);
}
function generateStatusBar(id, table, vm, cellStyleAttrs) {
if (getObject(id)) {
let statusColor = getState(id).val === 'running' ? colorOnline : colorOffline;
table.push({
seperator: Object.assign({
cellStyleAttrs: cellStyleAttrs,
html: <hr style="background: linear-gradient(90deg, transparent 0%, ${statusColor} 30%, ${statusColor} 50%, ${statusColor} 70%, transparent 100%); border-width: 0; height: 1px;margin-top: 0; margin-bottom: 0;">
,
}, statusSeperator)
})
} else {
logDpNotExist(vm.targetChannel, id);
}
}
function getFormattedTimeStamp(val) {
let now = moment();
let daysDiff = now.startOf('day').diff(moment(val).startOf('day'), 'days');
let timeFormated = moment(val).format('ddd DD.MM. - HH:mm');
if (daysDiff === 0) {
timeFormated = `Heute - ${moment(val).format('HH:mm')}`;
} else if (daysDiff === 1) {
timeFormated = `Gestern - ${moment(val).format('HH:mm')}`;
} else if (daysDiff > 1 && daysDiff <= 6) {
timeFormated = `vor ${daysDiff} Tagen - ${moment(val).format('HH:mm')}`;
} else if (daysDiff === 7) {
timeFormated = `vor einer Woche - ${moment(val).format('HH:mm')}`;
}
return timeFormated;
}
function calculateCpuAverage(targetChannel, val, isNode = false) {
let id = ${idDatenpunktPrefix}.${idDatenPunktStrukturPrefix}.${isNode ? 'node' : 'vm'}.${targetChannel}.cpuLastValues
;
try {
if (existsState(id)) {
let letzteWerte = getState(id).val;
letzteWerte = letzteWerte.toString().split(',');
if (val > 0) {
letzteWerte.unshift(val);
} else {
letzteWerte.unshift(0);
}
if (letzteWerte.length > cpuAverageLastValues) {
letzteWerte.splice(cpuAverageLastValues);
}
setState(id, letzteWerte.join(','), true);
let sum = 0;
for (const value of letzteWerte) {
sum = sum + parseFloat(value);
}
mySetState(`${idDatenpunktPrefix}.${idDatenPunktStrukturPrefix}.${isNode ? 'node' : 'vm'}.${targetChannel}.cpuAverage`, mathjs.round(sum / letzteWerte.length, 0), 'number', 'Durchschnittle CPU Last');
} else {
mySetState(id, val.toString(), 'string', 'Durchschnittle CPU Last letzte 60 Werte');
}
} catch (err) {
console.error(`[calculateCpuAverage] '${id}' - error: ${err.message}, stack: ${err.stack}`);
}
}
function mySetState(id, val, type, name, write = false) {
if (existsState(id)) {
setState(id, val, true);
} else {
createState(id, {
'name': name,
'type': type,
'read': true,
'write': write
}, function () {
setState(id, val, true);
});
}
}
function getUsedOfText(usedId, totalId, targetChannel) {
let text = 'N/A'
let used = existsState(usedId) ? getState(usedId).val : logDpNotExist(targetChannel, usedId);
let total = existsState(totalId) ? getState(totalId).val : logDpNotExist(targetChannel, totalId);
if (used && total) {
text = `${formatValue(used / 1024, 2, '.,')} GB / ${formatValue(total / 1024, 0, '.,')} GB`
}
return text;
}
function getHtmlTwoLines(text1, text2) {
return <div style="font-size: ${fontSizeSecondary}px; text-align: left; font-family: ${fontFamilySecondary}; color: ${colorPrimary}; line-height: 1.2; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">${text1}</div> <div style="font-size: ${fontSizeQuinary}px; text-align: left; font-family: ${fontFamilyQuaternary}; color: ${colorSecondary}; line-height: 1.2">${text2}</div>
}
function logDpNotExist(target, id) {
console.warn([updateVm - ${target}] datapoint '${id}' not exist!
);
}
// Bei JS Start ausführen
updateData();