- ioBroker Community Home
- Deutsch
- Tester
- Beta Test js-controller Kiera (v6.0)
NEWS
Beta Test js-controller Kiera (v6.0)
-
@uv-on-fire mit controller beta bitte auch javascript beta nutzen (8.6.0 aktuell)
@foxriver76
Hast natürlich recht. Habe soviel rumptobiert und auch mal eine ältere Version wieder installiert. Dachte ich hätte auch den controller wieder zurückgesetzt aber das ahbe ich wohl vergessen.Macht aber keinen Unterschied. Hier das aktuelle log nach wieder update auf 8.6.0
2024-06-17 16:30:07.577 - error: javascript.0 (3014300) script.js.Materialdesign.StatusAdapter: [adapterStatus] error: moment.duration(...).format is not a function, stack: TypeError: moment.duration(...).format is not a function
at Object.adapterStatus (script.js.Materialdesign.StatusAdapter:2370:115)
at Object.callback (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1427:38)
at Object.stateChange (/opt/iobroker/node_modules/iobroker.javascript/main.js:644:29)
at Immediate._onImmediate (/opt/iobroker/node_modules/@iobroker/js-controller-adapter/src/lib/adapter/adapter.ts:11043:62)
at processImmediate (node:internal/timers:476:21)
-
@foxriver76
Hast natürlich recht. Habe soviel rumptobiert und auch mal eine ältere Version wieder installiert. Dachte ich hätte auch den controller wieder zurückgesetzt aber das ahbe ich wohl vergessen.Macht aber keinen Unterschied. Hier das aktuelle log nach wieder update auf 8.6.0
2024-06-17 16:30:07.577 - error: javascript.0 (3014300) script.js.Materialdesign.StatusAdapter: [adapterStatus] error: moment.duration(...).format is not a function, stack: TypeError: moment.duration(...).format is not a function
at Object.adapterStatus (script.js.Materialdesign.StatusAdapter:2370:115)
at Object.callback (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1427:38)
at Object.stateChange (/opt/iobroker/node_modules/iobroker.javascript/main.js:644:29)
at Immediate._onImmediate (/opt/iobroker/node_modules/@iobroker/js-controller-adapter/src/lib/adapter/adapter.ts:11043:62)
at processImmediate (node:internal/timers:476:21)
@uv-on-fire Hilft dir das https://github.com/ioBroker/ioBroker.javascript/issues/1613
Poste da gerne auch mal dein Skript rein.
Hier könnt ihr mich unterstützen.
-
@foxriver76 @haus-automatisierung
Danke für den Fix mit den NPM Modulen und den hint mit demmomentDurationFormatSetup(moment);Jetzt läuft mit cotroller 6.0.5 und java 8.6.0 wieder alles einwandfrei.
-
@uv-on-fire Hilft dir das https://github.com/ioBroker/ioBroker.javascript/issues/1613
Poste da gerne auch mal dein Skript rein.
@foxriver76
Sorry für die verspätete Antwort aber wie gesagt bin morgen für längere Zeit weg und muss noch einiges regeln bis dahin.Also der Fix hat funktioniert.
Interessanterweise hat es genügt das in einem der Skripte ein zu tragen und jetzt laufen alle.
Jetzt motz noch ein anderes skript mathjs an
vascript.0
2024-06-17 21:31:59.693 error script.js.Materialdesign.Proxmox: [calculateCpuAverage] '0_userdata.0.Vis.MaterialDesignWidgets.Proxmox.vm.qemu_RaspiMatic.cpuLastValues' - error: Cannot read properties of undefined (reading 'round'), stack: TypeError: Cannot read properties of undefined (reading 'round') at calculateCpuAverage (script.js.Materialdesign.Proxmox:2891:140) at generateProgressBarCpuRow (script.js.Materialdesign.Proxmox:2791:9) at updateVm (script.js.Materialdesign.Proxmox:2598:9) at updateData (script.js.Materialdesign.Proxmox:2530:9) at script.js.Materialdesign.Proxmox:2939:1 at script.js.Materialdesign.Proxmox:2945:3 at Script.runInContext (node:vm:133:12) at Script.runInNewContext (node:vm:138:17) at execute (/opt/iobroker/node_modules/iobroker.javascript/main.js:1968:23) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2215:13)
javascript.0
2024-06-17 21:31:59.692 error script.js.Materialdesign.Proxmox: [calculateCpuAverage] '0_userdata.0.Vis.MaterialDesignWidgets.Proxmox.vm.lxc_ioBroker.cpuLastValues' - error: Cannot read properties of undefined (reading 'round'), stack: TypeError: Cannot read properties of undefined (reading 'round') at calculateCpuAverage (script.js.Materialdesign.Proxmox:2891:140) at generateProgressBarCpuRow (script.js.Materialdesign.Proxmox:2791:9) at updateVm (script.js.Materialdesign.Proxmox:2598:9) at updateData (script.js.Materialdesign.Proxmox:2530:9) at script.js.Materialdesign.Proxmox:2939:1 at script.js.Materialdesign.Proxmox:2945:3 at Script.runInContext (node:vm:133:12) at Script.runInNewContext (node:vm:138:17) at execute (/opt/iobroker/node_modules/iobroker.javascript/main.js:1968:23) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2215:13)
javascript.0
2024-06-17 21:31:59.689 error script.js.Materialdesign.Proxmox: [calculateCpuAverage] '0_userdata.0.Vis.MaterialDesignWidgets.Proxmox.node.promox.cpuLastValues' - error: Cannot read properties of undefined (reading 'round'), stack: TypeError: Cannot read properties of undefined (reading 'round') at calculateCpuAverage (script.js.Materialdesign.Proxmox:2891:140) at generateProgressBarCpuRow (script.js.Materialdesign.Proxmox:2791:9) at updateVm (script.js.Materialdesign.Proxmox:2598:9) at updateData (script.js.Materialdesign.Proxmox:2526:9) at script.js.Materialdesign.Proxmox:2939:1 at script.js.Materialdesign.Proxmox:2945:3 at Script.runInContext (node:vm:133:12) at Script.runInNewContext (node:vm:138:17) at execute (/opt/iobroker/node_modules/iobroker.javascript/main.js:1968:23) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2215:13)
javascript.0
2024-06-17 21:31:59.686 error at Script.runInContext (node:vm:133:12)
javascript.0
2024-06-17 21:31:59.686 error at script.js.Materialdesign.Proxmox:789:3
javascript.0
2024-06-17 21:31:59.686 error at script.js.Materialdesign.Proxmox:359:16
javascript.0
2024-06-17 21:31:59.686 error at require (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:548:32)
javascript.0
2024-06-17 21:31:59.686 error at require (node:internal/modules/helpers:177:18)
javascript.0
2024-06-17 21:31:59.686 error at Module.require (node:internal/modules/cjs/loader:1231:19)
javascript.0
2024-06-17 21:31:59.686 error script.js.Materialdesign.Proxmox: Error: Cannot find module 'mathjs'
Das skript dazu:
/************************************************************************************************************************************************************************
Version: 1.0.2
created by ScroungerDieses 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 ---
- Support: https://forum.iobroker.net/topic/35296/material-design-widgets-proxmox
- Github: https://github.com/Scrounger/ioBroker.vis-materialdesign/tree/master/examples/Proxmox
=========================================================================================================================================================================
--- 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 Prefixlet 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();Aber das Skript ist jetzt nicht essentiell wichtig. Notfalls schalt ich das ab bis ich wieder da bin.
-
@foxriver76
Sorry für die verspätete Antwort aber wie gesagt bin morgen für längere Zeit weg und muss noch einiges regeln bis dahin.Also der Fix hat funktioniert.
Interessanterweise hat es genügt das in einem der Skripte ein zu tragen und jetzt laufen alle.
Jetzt motz noch ein anderes skript mathjs an
vascript.0
2024-06-17 21:31:59.693 error script.js.Materialdesign.Proxmox: [calculateCpuAverage] '0_userdata.0.Vis.MaterialDesignWidgets.Proxmox.vm.qemu_RaspiMatic.cpuLastValues' - error: Cannot read properties of undefined (reading 'round'), stack: TypeError: Cannot read properties of undefined (reading 'round') at calculateCpuAverage (script.js.Materialdesign.Proxmox:2891:140) at generateProgressBarCpuRow (script.js.Materialdesign.Proxmox:2791:9) at updateVm (script.js.Materialdesign.Proxmox:2598:9) at updateData (script.js.Materialdesign.Proxmox:2530:9) at script.js.Materialdesign.Proxmox:2939:1 at script.js.Materialdesign.Proxmox:2945:3 at Script.runInContext (node:vm:133:12) at Script.runInNewContext (node:vm:138:17) at execute (/opt/iobroker/node_modules/iobroker.javascript/main.js:1968:23) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2215:13)
javascript.0
2024-06-17 21:31:59.692 error script.js.Materialdesign.Proxmox: [calculateCpuAverage] '0_userdata.0.Vis.MaterialDesignWidgets.Proxmox.vm.lxc_ioBroker.cpuLastValues' - error: Cannot read properties of undefined (reading 'round'), stack: TypeError: Cannot read properties of undefined (reading 'round') at calculateCpuAverage (script.js.Materialdesign.Proxmox:2891:140) at generateProgressBarCpuRow (script.js.Materialdesign.Proxmox:2791:9) at updateVm (script.js.Materialdesign.Proxmox:2598:9) at updateData (script.js.Materialdesign.Proxmox:2530:9) at script.js.Materialdesign.Proxmox:2939:1 at script.js.Materialdesign.Proxmox:2945:3 at Script.runInContext (node:vm:133:12) at Script.runInNewContext (node:vm:138:17) at execute (/opt/iobroker/node_modules/iobroker.javascript/main.js:1968:23) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2215:13)
javascript.0
2024-06-17 21:31:59.689 error script.js.Materialdesign.Proxmox: [calculateCpuAverage] '0_userdata.0.Vis.MaterialDesignWidgets.Proxmox.node.promox.cpuLastValues' - error: Cannot read properties of undefined (reading 'round'), stack: TypeError: Cannot read properties of undefined (reading 'round') at calculateCpuAverage (script.js.Materialdesign.Proxmox:2891:140) at generateProgressBarCpuRow (script.js.Materialdesign.Proxmox:2791:9) at updateVm (script.js.Materialdesign.Proxmox:2598:9) at updateData (script.js.Materialdesign.Proxmox:2526:9) at script.js.Materialdesign.Proxmox:2939:1 at script.js.Materialdesign.Proxmox:2945:3 at Script.runInContext (node:vm:133:12) at Script.runInNewContext (node:vm:138:17) at execute (/opt/iobroker/node_modules/iobroker.javascript/main.js:1968:23) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2215:13)
javascript.0
2024-06-17 21:31:59.686 error at Script.runInContext (node:vm:133:12)
javascript.0
2024-06-17 21:31:59.686 error at script.js.Materialdesign.Proxmox:789:3
javascript.0
2024-06-17 21:31:59.686 error at script.js.Materialdesign.Proxmox:359:16
javascript.0
2024-06-17 21:31:59.686 error at require (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:548:32)
javascript.0
2024-06-17 21:31:59.686 error at require (node:internal/modules/helpers:177:18)
javascript.0
2024-06-17 21:31:59.686 error at Module.require (node:internal/modules/cjs/loader:1231:19)
javascript.0
2024-06-17 21:31:59.686 error script.js.Materialdesign.Proxmox: Error: Cannot find module 'mathjs'
Das skript dazu:
/************************************************************************************************************************************************************************
Version: 1.0.2
created by ScroungerDieses 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 ---
- Support: https://forum.iobroker.net/topic/35296/material-design-widgets-proxmox
- Github: https://github.com/Scrounger/ioBroker.vis-materialdesign/tree/master/examples/Proxmox
=========================================================================================================================================================================
--- 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 Prefixlet 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();Aber das Skript ist jetzt nicht essentiell wichtig. Notfalls schalt ich das ab bis ich wieder da bin.
@uv-on-fire Das mit mathjs wird in der nächsten javascript Adapter Version behoben sein (>8.6.0 sobald verfügbar)
Hier könnt ihr mich unterstützen.
-
Der Adapter BlueConnect funktioniert leider auch nicht mehr mit dem neuen JS-Controller
@diamand2k22 sagte in Beta Test js-controller Kiera (v6.0):
Der Adapter BlueConnect funktioniert leider auch nicht mehr mit dem neuen JS-Controller
Teste mal neu.. (von Github) Kopierschnitte hat soviel ich weiss angepasst..
Da ich selbst keinen Blueconnect mehr benutze liegt das Projekt ein bisschen.
Grüße,
Martin
-
@diamand2k22 sagte in Beta Test js-controller Kiera (v6.0):
Der Adapter BlueConnect funktioniert leider auch nicht mehr mit dem neuen JS-Controller
Teste mal neu.. (von Github) Kopierschnitte hat soviel ich weiss angepasst..
Da ich selbst keinen Blueconnect mehr benutze liegt das Projekt ein bisschen.
@mameier1234 Die Frage ist funktioniert er mit controller 6.0.5 wieder wie er ist @Diamand2k22
Zumindest startet er bei mir wieder sauber, zur Funktionalität kann ich nix sagen.
Hier könnt ihr mich unterstützen.
-
@mameier1234 Die Frage ist funktioniert er mit controller 6.0.5 wieder wie er ist @Diamand2k22
Zumindest startet er bei mir wieder sauber, zur Funktionalität kann ich nix sagen.
@foxriver76 Mangels Testsystem für JS6 kann ich es dort nicht testen, aber die Funktionalität ist, zumindest unter JS5 gegebenen.
Der Blueconnect-Adapter sollte daher funktionieren. Vielleicht kann @Diamand2k22 berichten...
-
@mameier1234 Die Frage ist funktioniert er mit controller 6.0.5 wieder wie er ist @Diamand2k22
Zumindest startet er bei mir wieder sauber, zur Funktionalität kann ich nix sagen.
@foxriver76
ich hab gesehen, dass du beim echarts-Adapter Änderungen vorgenommen hast1.8.3 (2024-06-17) (foxriver76) upgraded dependencieswann wird die Version verfügbar sein?
-
@foxriver76
ich hab gesehen, dass du beim echarts-Adapter Änderungen vorgenommen hast1.8.3 (2024-06-17) (foxriver76) upgraded dependencieswann wird die Version verfügbar sein?
-
@latzi echarts 1.8.3 fixt das Problem wohl leider auch nicht, Bluefox arbeitet aktuell an dem Problem, ist was größeres leider. Nach Möglichkeit die stable version nutzen die sollte tun.
@foxriver76
vielen Dank für die Info, bin aktuell ohnehin auf 1.7.2, die läuft ja problemlos mit js-controller 5 -
@foxriver76
vielen Dank für die Info, bin aktuell ohnehin auf 1.7.2, die läuft ja problemlos mit js-controller 5 -
Ich weiß nicht, ob das Problem nur unter Windows auftritt. Ich habe gerade nochmal von 5.0.19 direkt auf 6.0.5 upgegraded.
Anschließend fährt der ioBroker leider nicht mehr hoch. Im Service Error Log steht:node:internal/modules/esm/resolve:304 return new ERR_PACKAGE_PATH_NOT_EXPORTED( ^ Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './tools' is not defined by "exports" in C:\ioBroker\SmartHome10\node_modules\@iobroker\js-controller-common\package.json at exportsNotFound (node:internal/modules/esm/resolve:304:10) at packageExportsResolve (node:internal/modules/esm/resolve:651:9) at resolveExports (node:internal/modules/cjs/loader:592:36) at Module._findPath (node:internal/modules/cjs/loader:669:31) at Module._resolveFilename (node:internal/modules/cjs/loader:1131:27) at Module._load (node:internal/modules/cjs/loader:986:27) at Module.require (node:internal/modules/cjs/loader:1233:19) at require (node:internal/modules/helpers:179:18) at Object.<anonymous> (C:\ioBroker\SmartHome10\node_modules\iobroker.js-controller\build\lib\restart.js:8:17) at Module._compile (node:internal/modules/cjs/loader:1358:14) { code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' }iob fix behebt das Problem, wobei ich im Moment noch nicht weiß, warum es nach iob fix wieder funktioniert.
@foxriver76
Hast du schon eine Idee oder brauchst du noch weitere Infos? -
Ich weiß nicht, ob das Problem nur unter Windows auftritt. Ich habe gerade nochmal von 5.0.19 direkt auf 6.0.5 upgegraded.
Anschließend fährt der ioBroker leider nicht mehr hoch. Im Service Error Log steht:node:internal/modules/esm/resolve:304 return new ERR_PACKAGE_PATH_NOT_EXPORTED( ^ Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './tools' is not defined by "exports" in C:\ioBroker\SmartHome10\node_modules\@iobroker\js-controller-common\package.json at exportsNotFound (node:internal/modules/esm/resolve:304:10) at packageExportsResolve (node:internal/modules/esm/resolve:651:9) at resolveExports (node:internal/modules/cjs/loader:592:36) at Module._findPath (node:internal/modules/cjs/loader:669:31) at Module._resolveFilename (node:internal/modules/cjs/loader:1131:27) at Module._load (node:internal/modules/cjs/loader:986:27) at Module.require (node:internal/modules/cjs/loader:1233:19) at require (node:internal/modules/helpers:179:18) at Object.<anonymous> (C:\ioBroker\SmartHome10\node_modules\iobroker.js-controller\build\lib\restart.js:8:17) at Module._compile (node:internal/modules/cjs/loader:1358:14) { code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' }iob fix behebt das Problem, wobei ich im Moment noch nicht weiß, warum es nach iob fix wieder funktioniert.
@foxriver76
Hast du schon eine Idee oder brauchst du noch weitere Infos? -
@gaspode hm das ist komisch, die restart.js muss von einem alten controller gewesen sein ... im aktuellen gibt es den import nicht..
War von einer frisch installierten Version 5.0.19 auf 6.0.5.
Ich such morgen mal nach der Ursache. -
War von einer frisch installierten Version 5.0.19 auf 6.0.5.
Ich such morgen mal nach der Ursache.@gaspode ja laut der Fehlermeldung wird bei dir noch irgendwas von
js-controller-common/toolsprobiert zu importieren aber im code auf npm ist das schonjs-controller-common-db/tools. Also irgendwas passt da nicht ganz mit dem Code der da lief. Hast du evtl. den Controller aktualisiert während er lief? Und dann beim ersten restart evtl. noch gecachtes File..Hier könnt ihr mich unterstützen.
-
@gaspode ja laut der Fehlermeldung wird bei dir noch irgendwas von
js-controller-common/toolsprobiert zu importieren aber im code auf npm ist das schonjs-controller-common-db/tools. Also irgendwas passt da nicht ganz mit dem Code der da lief. Hast du evtl. den Controller aktualisiert während er lief? Und dann beim ersten restart evtl. noch gecachtes File..@foxriver76 said in Beta Test js-controller Kiera (v6.0):
Hast du evtl. den Controller aktualisiert während er lief?
Nee, ioBroker war gestoppt. Und das Problem war auch dauerhaft.
Wie gesagt, ich guck mal morgen etwas genauer. -
@foxriver76 said in Beta Test js-controller Kiera (v6.0):
Hast du evtl. den Controller aktualisiert während er lief?
Nee, ioBroker war gestoppt. Und das Problem war auch dauerhaft.
Wie gesagt, ich guck mal morgen etwas genauer.@gaspode ah und allgemein
das ganze File in dem der Error aufkommt
at Object.<anonymous> (C:\ioBroker\SmartHome10\node_modules\iobroker.js-controller\build\lib\restart.js:8:17)Das gibt es in Controller v6 gar nicht... da ist die Struktur anders..
Bin gespannt ob du was raus findest. Irgendwie hat er da noch 5.0.19 Code ausgeführt
Hier könnt ihr mich unterstützen.
-
@gaspode ah und allgemein
das ganze File in dem der Error aufkommt
at Object.<anonymous> (C:\ioBroker\SmartHome10\node_modules\iobroker.js-controller\build\lib\restart.js:8:17)Das gibt es in Controller v6 gar nicht... da ist die Struktur anders..
Bin gespannt ob du was raus findest. Irgendwie hat er da noch 5.0.19 Code ausgeführt
@foxriver76 said in Beta Test js-controller Kiera (v6.0):
Das gibt es in Controller v6 gar nicht... da ist die Struktur anders..
Nach iob fix ist das ganze lib Verzeichnis auch nicht mehr da. :D
Die Frage ist, warum er da rein läuft. -
@foxriver76 said in Beta Test js-controller Kiera (v6.0):
Das gibt es in Controller v6 gar nicht... da ist die Struktur anders..
Nach iob fix ist das ganze lib Verzeichnis auch nicht mehr da. :D
Die Frage ist, warum er da rein läuft.OK, irgendwas geht bei
iob upgrade selfschief, wen direkt von 5.0.19 auf 6.0.5 aktualisiert wird:
>npm ls npm error code ELSPROBLEMS npm error invalid: iobroker.js-controller@5.0.19 C:\ioBroker\SmartHome11\node_modules\iobroker.js-controller iobroker.inst@3.0.0 C:\ioBroker\SmartHome11 +-- dotenv@16.3.1 +-- iobroker.admin@6.13.16 +-- iobroker.backitup@2.11.0 +-- iobroker.discovery@4.5.0 +-- iobroker.js-controller@5.0.19 invalid: "6.0.5" from the root project +-- node-windows@1.0.0-beta.8 `-- windows-shortcuts@0.1.6Ausführen von
npm irichtet das zwar wieder, aber der Anspruch war ja, dass
iob upgrade selfohne weitere Aktionen ausreichen soll. Evtl. kann ja mal jemand in einer Linux Umgebung testen, ob das Problem da auch auftritt.
Zumindest unter Windows ist das zuverlässig reproduzierbar.