Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. script Portainer Api V3.3 - inkl. html Tabelle

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.5k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.1k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    16
    1
    2.7k

script Portainer Api V3.3 - inkl. html Tabelle

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
blocklyjavascriptmonitoring
14 Beiträge 3 Kommentatoren 600 Aufrufe 3 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • ilovegymI ilovegym

    @david-g @Codierknecht

    hab mal meinen "Sheldon" :) gefragt (Chatgpt), und das kam zum schluss raus, habs noch n bisserl verfeinert, da die ersten Ansaetze nix waren..

    Das Script fragt direkt die Portainer-Api ab, einen Api-Key braucht man nicht und legt die Datenpunkte fuer Status, Ram, CPU und uptime im gewuenschten Ordner an.
    Man kann den/die Container starten/stoppen.

    Script in den ersten Beitrag verschoben, dieser wird immer aktualisiert.

    David G.D Online
    David G.D Online
    David G.
    schrieb am zuletzt editiert von David G.
    #5

    @ilovegym

    Hat sich erledigt.
    Auch meine Nachricht.

    Zeigt eure Lovelace-Visualisierung klick
    (Auch ideal um sich Anregungen zu holen)

    Meine Tabellen für eure Visualisierung klick

    ilovegymI 1 Antwort Letzte Antwort
    0
    • David G.D David G.

      @ilovegym

      Hat sich erledigt.
      Auch meine Nachricht.

      ilovegymI Online
      ilovegymI Online
      ilovegym
      schrieb am zuletzt editiert von
      #6

      @david-g

      alles gut, bin gerade am erweitern / optimieren des Scripts..

      David G.D 1 Antwort Letzte Antwort
      0
      • ilovegymI ilovegym

        @david-g

        alles gut, bin gerade am erweitern / optimieren des Scripts..

        David G.D Online
        David G.D Online
        David G.
        schrieb am zuletzt editiert von
        #7

        @ilovegym

        Bei mir möchte es auch nicht ganz.

        avascript.0	14:28:18.301	info	
        Stopping script script.js.Eigene_Scripte.Portainer
        javascript.0	14:28:18.359	info	
        Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
        javascript.0	14:28:18.382	info	
        registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
        javascript.0	14:28:18.470	error	
        ❌ Fehler beim Parsen der Containerdaten: containers.forEach is not a function
        

        Zeigt eure Lovelace-Visualisierung klick
        (Auch ideal um sich Anregungen zu holen)

        Meine Tabellen für eure Visualisierung klick

        ilovegymI 1 Antwort Letzte Antwort
        0
        • David G.D David G.

          @ilovegym

          Bei mir möchte es auch nicht ganz.

          avascript.0	14:28:18.301	info	
          Stopping script script.js.Eigene_Scripte.Portainer
          javascript.0	14:28:18.359	info	
          Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
          javascript.0	14:28:18.382	info	
          registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
          javascript.0	14:28:18.470	error	
          ❌ Fehler beim Parsen der Containerdaten: containers.forEach is not a function
          
          ilovegymI Online
          ilovegymI Online
          ilovegym
          schrieb am zuletzt editiert von
          #8

          @david-g

          endpointid richtig? muss nicht 1 sein,bei ist es 2

          David G.D 1 Antwort Letzte Antwort
          0
          • ilovegymI ilovegym

            @david-g

            endpointid richtig? muss nicht 1 sein,bei ist es 2

            David G.D Online
            David G.D Online
            David G.
            schrieb am zuletzt editiert von
            #9

            @ilovegym
            Hab 2 getestet. Dann kommt keinen token erhalten. Dann wieder 1. Kommt jetzt auch keinen token erhalten.

            Zeigt eure Lovelace-Visualisierung klick
            (Auch ideal um sich Anregungen zu holen)

            Meine Tabellen für eure Visualisierung klick

            ilovegymI 1 Antwort Letzte Antwort
            0
            • David G.D David G.

              @ilovegym
              Hab 2 getestet. Dann kommt keinen token erhalten. Dann wieder 1. Kommt jetzt auch keinen token erhalten.

              ilovegymI Online
              ilovegymI Online
              ilovegym
              schrieb am zuletzt editiert von
              #10

              @david-g

              hmm vielleicht Portainer wegen zuvielen falschen Zugriffen geblockt, war bei mir auch am Anfang, als das Script noch fehler hatte.. einfach Portainer neu starten...

              David G.D 1 Antwort Letzte Antwort
              0
              • ilovegymI ilovegym

                @david-g

                hmm vielleicht Portainer wegen zuvielen falschen Zugriffen geblockt, war bei mir auch am Anfang, als das Script noch fehler hatte.. einfach Portainer neu starten...

                David G.D Online
                David G.D Online
                David G.
                schrieb am zuletzt editiert von David G.
                #11

                @ilovegym

                Die ganzen warns waren mit id 2, der Fehler beim Pharsen mit 1.

                1.8.2025, 14:43:04.923	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:43:05.015	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:43:05.037	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:43:05.045	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:43:54.160	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:43:54.208	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:43:54.230	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:43:54.239	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:43:58.696	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:44:01.907	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:44:01.913	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:44:01.918	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:44:15.888	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:44:15.978	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:44:15.986	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:44:15.994	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:45:00.067	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:46:35.734	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:46:35.824	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:46:35.844	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:46:35.851	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:52:03.359	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:52:03.450	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:52:03.474	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:52:03.481	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:52:16.268	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:52:16.358	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:52:16.377	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:52:16.395	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 14:52:29.449	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 14:52:29.537	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 14:52:29.561	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 14:52:29.570	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Kein Token erhalten
                1.8.2025, 15:01:54.921	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 15:01:55.010	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 15:01:55.023	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 15:01:55.205	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:55.205	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:55.206	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:55.206	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:55.206	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:55.208	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:55.208	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:55.208	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:55.208	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:55.209	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:55.209	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:55.209	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:56.195	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:56.196	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.205	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.206	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.211	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.213	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.218	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.224	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.231	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.233	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.235	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.236	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.236	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.237	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.240	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.240	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:01:57.241	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:96:25
                1.8.2025, 15:01:57.241	[warn ]: javascript.0 (1615949)     at script.js.Eigene_Scripte.Portainer:99:25
                1.8.2025, 15:02:14.020	[info ]: javascript.0 (1615949) Stopping script script.js.Eigene_Scripte.Portainer
                1.8.2025, 15:02:14.111	[info ]: javascript.0 (1615949) Start JavaScript script.js.Eigene_Scripte.Portainer (Javascript/js)
                1.8.2025, 15:02:14.130	[info ]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: registered 1 subscription, 1 schedule, 0 messages, 0 logs and 0 file subscriptions
                1.8.2025, 15:02:14.221	[error]: javascript.0 (1615949) script.js.Eigene_Scripte.Portainer: ❌ Fehler beim Parsen der Containerdaten: containers.forEach is not a function
                

                Portainer 2.27.9

                Edit
                Evtl kann man den Endpoint automatisch ausfüllen, wenn man diesen leer lässt.

                curl -H "Authorization: Bearer <JWT>" \
                     http://10.68.xx.zzz:9000/api/endpoints
                

                und dann den ersten falls es mehrere gibt.

                Zeigt eure Lovelace-Visualisierung klick
                (Auch ideal um sich Anregungen zu holen)

                Meine Tabellen für eure Visualisierung klick

                1 Antwort Letzte Antwort
                0
                • ilovegymI ilovegym

                  Edit: Header, Text, Scripte :). vielen Dank an alle die mir hier geholfen haben!

                  Hallo,
                  wie bekomme ich die Daten aus Portainer in den iobroker?

                  Edit:
                  Hier das, was chatgpt nach einigen Versuchen mir ausgegeben hat, das Javascript fragt die Portainer-Api in regelmaessigen Abstaenden ab, man kann Docker Container starten/stoppen, bekommt bei Aenderungen eine Msg per Discord, und es werden Cpu, Ram, Uptime, Image, IP, Port angezeigt:

                  // 📦 Portainer Statusboard Version 3.3
                  // by Ilovegym
                  // 📜 Änderungsprotokoll
                  // - Version 3.3: Mobile Ansicht – CPU/RAM ein-/ausblendbar
                  // - Version 3.2: Mobile Ansicht mit Symbolstatus & 2-zeiligem Layout konfigurierbar
                  // - Version 3.1: Mobiler Dashboard-Datenpunkt konfigurierbar
                  // - Version 3.0: Mobiles Dashboard in Datenpunkt StatusMobile
                  // - Version 2.7: Persistenter Theme-Switch
                  // - Version 2.6: Persistente Filter und zusätzliche Filterfelder (CPU, RAM, Alter)
                  // - Version 2.5: Filter für laufende Container eingebaut
                  // - Version 2.4: "Erstellt" zeigt nun Alter in Tagen an
                  // - Version 2.3: Alphabetische Standard-Sortierung
                  // - Version 2.2: Warn-Icons, Farb-Balken für CPU/RAM, letzter Restart-Zeitpunkt
                  // - Version 2.1: CPU und RAM korrekt aus Stats übernommen
                  // - Version 2.0: Hinzugefügt: Restarted-Spalte, Tooltip mit IP, Theme-Switch, Sortierung
                  // - Version 1.x: Basisfunktion: Containerdatenpunkte, Discord/Telegram Benachrichtigungen, Statusboard
                  const exec = require('child_process').exec;
                  
                  // 🔧 KONFIGURATION
                  const mobileLayoutColumns = 1; // 1 = eine Spalte, 2 = zwei Spalten in mobiler Ansicht
                  
                  const mobileColumnOrder = [
                      'name', 'state', 'cpu', 'mem', 'uptime'
                  ]; // Spaltenreihenfolge in der mobilen Ansicht
                  
                  const tableColumnOrder = [
                      'name', 'cpu', 'mem', 'state', 'uptime', 'restartedRecently', 'restartAt', 'image', 'created', 'ports'
                  ]; // Reihenfolge der Spalten im Statusboard
                  
                  const username = 'admin';
                  const password = 'supergeheimespassword!';
                  const portainerHost = 'http://10.10.2.10:9000';
                  const endpointId = 2;  //default 1
                  const datapointBase = '0_userdata.0.Portainer.Containers.';
                  const statusBoardDP = '0_userdata.0.Portainer.Containers.Statusboard';
                  const updateIntervalMinutes = 3;
                  const mobileStatusDP = '0_userdata.0.Portainer.Containers.StatusMobile';
                  const mobileUseSymbol = true;       // true = ✅/❌, false = 'running'
                  const mobileShowCPU = true;         // true = CPU in mobiler Ansicht anzeigen
                  const mobileShowRAM = true;         // true = RAM in mobiler Ansicht anzeigen
                  const mobileTwoLineLayout = true;   // true = Name/Status oben, Details unten
                  const discordDP = 'discord.0.xxxx.send'; //bitte anpassen
                  
                  const discordToggleDP = '0_userdata.0.Portainer.Notify.Discord';
                  const telegramToggleDP = '0_userdata.0.Portainer.Notify.Telegram';
                  const telegramInstance = 'telegram.0';
                  
                  // Initiale Schalter
                  if (!existsState(discordToggleDP)) {
                      createState(discordToggleDP, true, { type: 'boolean', name: 'Discord aktivieren', read: true, write: true });
                  }
                  if (!existsState(telegramToggleDP)) {
                      createState(telegramToggleDP, false, { type: 'boolean', name: 'Telegram aktivieren', read: true, write: true });
                  }
                  
                  function getTokenAndContainers() {
                      const loginCmd = `curl -s -H "Content-Type: application/json" -X POST -d '{"Username":"${username}","Password":"${password}"}' ${portainerHost}/api/auth`;
                      exec(loginCmd, (error, stdout) => {
                          if (error) return log(`❌ Token-Fehler: ${error}`, 'error');
                          try {
                              const token = JSON.parse(stdout).jwt;
                              if (!token) return log('❌ Kein Token erhalten', 'error');
                              getContainerList(token);
                          } catch (e) {
                              log(`❌ Token-Parsing-Fehler: ${e.message}`, 'error');
                          }
                      });
                  }
                  
                  function getContainerList(token) {
                      const url = `${portainerHost}/api/endpoints/${endpointId}/docker/containers/json?all=1`;
                      const cmd = `curl -s -H "Authorization: Bearer ${token}" "${url}"`;
                      exec(cmd, (error, stdout) => {
                          if (error) return log(`❌ Fehler beim Containerabruf: ${error}`, 'error');
                  
                          try {
                              const containers = JSON.parse(stdout);
                              const containerList = [];
                  
                              containers.forEach(container => {
                                  const id = container.Id;
                                  const name = container.Names[0].replace(/^\//, '');
                                  const dp = `${datapointBase}${name}`;
                  
                                  const image = container.Image;
                                  const createdDate = new Date(container.Created * 1000);
                                  const createdDaysAgo = Math.floor((Date.now() - createdDate.getTime()) / (1000 * 60 * 60 * 24));
                                  const created = `${createdDate.toLocaleString('de-DE', { hour12: false })} (${createdDaysAgo} Tage alt)`;
                                  const ports = (container.Ports || []).map(p => `${p.IP || '0.0.0.0'}:${p.PublicPort}->${p.PrivatePort}/${p.Type}`).join(', ');
                                  const ip = container.NetworkSettings?.Networks?.bridge?.IPAddress || 'n/a';
                  
                                  createState(`${dp}.image`, image, true);
                                  setState(`${dp}.image`, image, true);
                                  createState(`${dp}.created`, created, true);
                                  setState(`${dp}.created`, created, true);
                                  createState(`${dp}.ports`, ports, true);
                                  setState(`${dp}.ports`, ports, true);
                                  createState(`${dp}.ip`, ip, true);
                                  setState(`${dp}.ip`, ip, true);
                                  createState(`${dp}.control`, false, { type: 'boolean', role: 'button', write: true, read: false });
                  
                                  const detailUrl = `${portainerHost}/api/endpoints/${endpointId}/docker/containers/${id}/json`;
                                  const detailCmd = `curl -s -H "Authorization: Bearer ${token}" "${detailUrl}"`;
                                  exec(detailCmd, (err, detailOut) => {
                                      if (err) return log(`❌ Fehler bei Details für ${name}: ${err}`, 'warn');
                                      try {
                                          const detail = JSON.parse(detailOut);
                                          const isRunning = detail?.State?.Running === true;
                                          const state = isRunning ? 'running' : 'exited';
                                          const startedAt = new Date(isRunning ? detail.State.StartedAt : 0);
                                          const now = new Date();
                                          const uptimeMs = isRunning ? now - startedAt : 0;
                                          const uptimeText = formatUptime(uptimeMs);
                  
                                          const stateDP = `${dp}.state`;
                                          getStateAsync(stateDP).then(oldState => {
                                              if (oldState && oldState.val !== state) {
                                                  const msg = `🔔 Container \`${name}\` Status: \`${oldState.val}\` → \`${state}\``;
                                                  sendDiscordMessage(msg);
                                                  sendTelegramMessage(msg);
                                              }
                                          });
                  
                                          createState(stateDP, state, true);
                                          setState(stateDP, state, true);
                                          createState(`${dp}.uptimeText`, uptimeText, true);
                                          setState(`${dp}.uptimeText`, uptimeText, true);
                  
                                          const statsCmd = `curl -s -H "Authorization: Bearer ${token}" "${portainerHost}/api/endpoints/${endpointId}/docker/containers/${id}/stats?stream=false"`;
                                          exec(statsCmd, (e, statsOut) => {
                                              if (e) return;
                                              try {
                                                  const stats = JSON.parse(statsOut);
                                                  const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage;
                                                  const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
                                                  const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * stats.cpu_stats.online_cpus * 100 : 0;
                                                  const memMB = Math.round((stats.memory_stats.usage || 0) / 1024 / 1024);
                  
                                                  createState(`${dp}.cpu`, Math.round(cpuPercent * 10) / 10, true);
                                                  setState(`${dp}.cpu`, Math.round(cpuPercent * 10) / 10, true);
                                                  createState(`${dp}.mem`, memMB, true);
                                                  setState(`${dp}.mem`, memMB, true);
                  
                                                  const cpuSafe = Math.round(cpuPercent * 10) / 10;
                                                  const memSafe = memMB;
                                                  const restartedRecently = isRunning && (uptimeMs < 2 * 60 * 1000);
                                                  const restartAt = isRunning ? startedAt.toLocaleString('de-DE', { hour12: false }) : '-';
                  containerList.push({ name, state, uptime: uptimeText, cpu: cpuSafe, mem: memSafe, image, ports, created, ip, restartedRecently, restartAt });
                  
                                                  setState(statusBoardDP, buildHTML(containerList), true);
                                                  setState(mobileStatusDP, buildMobileHTML(containerList), true);
                                              } catch (e) {
                                                  log(`⚠️ Stats-Parsing-Fehler ${name}: ${e.message}`, 'warn');
                                              }
                                          });
                  
                                          
                  
                                      } catch (e) {
                                          log(`❌ Detail-Parsing-Fehler ${name}: ${e.message}`, 'warn');
                                      }
                                  });
                              });
                          } catch (err) {
                              log(`❌ Containerdaten-Fehler: ${err.message}`, 'error');
                          }
                      });
                  }
                  
                  function formatUptime(ms) {
                      const totalSeconds = Math.floor(ms / 1000);
                      const days = Math.floor(totalSeconds / 86400);
                      const hours = Math.floor((totalSeconds % 86400) / 3600);
                      const minutes = Math.floor((totalSeconds % 3600) / 60);
                      let str = '';
                      if (days > 0) str += `${days}d `;
                      if (days > 0 || hours > 0) str += `${hours}h `;
                      str += `${minutes}min`;
                      return str.trim();
                  }
                  
                  on(new RegExp(`${datapointBase}(.*)\.control`), function (obj) {
                      if (obj.state.val !== true) return;
                      const name = obj.id.split('.')[4];
                      const loginCmd = `curl -s -H "Content-Type: application/json" -X POST -d '{"Username":"${username}","Password":"${password}"}' ${portainerHost}/api/auth`;
                      exec(loginCmd, (error, stdout) => {
                          if (error) return log(`❌ Loginfehler: ${error}`, 'error');
                          try {
                              const token = JSON.parse(stdout).jwt;
                              const listCmd = `curl -s -H "Authorization: Bearer ${token}" "${portainerHost}/api/endpoints/${endpointId}/docker/containers/json?all=1"`;
                              exec(listCmd, (err, out) => {
                                  const list = JSON.parse(out);
                                  const container = list.find(c => c.Names[0].replace(/^\//, '') === name);
                                  if (!container) return log(`⚠️ Container ${name} nicht gefunden`, 'warn');
                                  const action = container.State === 'running' ? 'stop' : 'start';
                                  const actionUrl = `${portainerHost}/api/endpoints/${endpointId}/docker/containers/${container.Id}/${action}`;
                                  const actionCmd = `curl -s -H "Authorization: Bearer ${token}" -X POST "${actionUrl}"`;
                                  exec(actionCmd, () => {
                                      log(`✅ Container ${name} ${action} ausgeführt`);
                                      setTimeout(() => getTokenAndContainers(), 3000);
                                      setTimeout(() => setState(obj.id, false, true), 500);
                                  });
                              });
                          } catch (e) {
                              log(`❌ Steuerungsfehler: ${e.message}`, 'error');
                          }
                      });
                  });
                  
                  function sendDiscordMessage(message) {
                      const discordEnabled = getState(discordToggleDP)?.val === true;
                      if (discordEnabled && existsState(discordDP)) {
                          setState(discordDP, message);
                          log(`📤 Discord: ${message}`);
                      } else {
                          log(`ℹ️ Discord deaktiviert oder nicht verfügbar`, 'info');
                      }
                  }
                  
                  function sendTelegramMessage(message) {
                      const telegramEnabled = getState(telegramToggleDP)?.val === true;
                      if (telegramEnabled) {
                          sendTo(telegramInstance, 'send', { text: message });
                          log(`📤 Telegram: ${message}`);
                      } else {
                          log(`ℹ️ Telegram deaktiviert`, 'info');
                      }
                  }
                  
                  function buildHTML(list) {
                      list.sort((a, b) => a.name.localeCompare(b.name));
                      let html = `<b style="color:#ffffff;">🧾 Docker Statusboard</b><br>
                      <label for="runningFilter" style="color:#ffffff;">🚦 Nur laufende anzeigen</label>
                      <input type="checkbox" id="runningFilter" onchange="filterRunning()" style="margin-bottom:10px;">
                      <label for="themeToggle" style="color:#ffffff;">🌗 Theme</label>
                      <input type="checkbox" id="themeToggle" checked onchange="toggleTheme()" style="margin-bottom: 10px;"><br>
                      <table id="dockerStatus" border="1" cellspacing="0" cellpadding="6"
                          style="font-family:Arial; font-size:13px; border-collapse:collapse;
                                 text-align:left; background:#1e1e1e; color:#ffffff; border-color:#444; width:100%;">
                          <thead>
                          <tr style="background:#333;">
                              <th onclick="sortTable(0)">Name</th>
                              <th onclick="sortTable(1)">CPU</th>
                              <th onclick="sortTable(2)">RAM</th>
                              <th onclick="sortTable(3)">Status</th>
                              <th onclick="sortTable(4)">Uptime</th>
                              <th onclick="sortTable(5)">Restarted</th>
                              <th onclick="sortTable(6)">Restart-Zeit</th>
                              <th onclick="sortTable(7)">Image</th>
                              <th onclick="sortTable(8)">Erstellt</th>
                              <th onclick="sortTable(9)">Ports</th>
                          </tr>
                          </thead><tbody>`;
                  
                      for (let c of list) {
                          html += `<tr style="background:${c.state === 'running' ? '#2d572c' : '#571b1b'};" title="IP: ${c.ip}">
                              <td>${c.name}</td>
                              <td style="color:${c.cpu > 50 ? '#ff5555' : '#cccccc'}">${c.cpu}</td>
                              <td style="color:${c.mem > 500 ? '#ff8800' : '#cccccc'}">${c.mem}</td>
                              <td>${c.state === 'running' ? '✅' : '❌'} ${c.state}</td>
                              <td>${c.uptime}</td>
                              <td style="color: ${c.restartedRecently ? '#ffaa00' : '#cccccc'};">${c.restartedRecently ? 'ja' : 'nein'}</td>
                              <td>${c.restartAt}</td>
                              <td>${c.image}</td>
                              <td>${c.created}</td>
                              <td>${c.ports}</td>
                          </tr>`;
                      }
                  
                      html += `</tbody></table>
                      <script>
                          function filterRunning() {
                              var table = document.getElementById("dockerStatus");
                              var rows = table.getElementsByTagName("tr");
                              var onlyRunning = document.getElementById("runningFilter").checked;
                              for (var i = 1; i < rows.length; i++) {
                                  var stateCell = rows[i].getElementsByTagName("td")[3];
                                  if (!stateCell) continue;
                                  var isRunning = stateCell.textContent.includes("running");
                                  rows[i].style.display = (onlyRunning && !isRunning) ? "none" : "";
                              }
                          }
                          function sortTable(n) {
                              var table = document.getElementById("dockerStatus");
                              var switching = true;
                              var dir = "asc";
                              var switchcount = 0;
                              while (switching) {
                                  switching = false;
                                  var rows = table.rows;
                                  for (var i = 1; i < rows.length - 1; i++) {
                                      var shouldSwitch = false;
                                      var x = rows[i].getElementsByTagName("TD")[n];
                                      var y = rows[i + 1].getElementsByTagName("TD")[n];
                                      var xContent = x.textContent || x.innerText;
                                      var yContent = y.textContent || y.innerText;
                                      var xNum = parseFloat(xContent);
                                      var yNum = parseFloat(yContent);
                                      var isNumber = !isNaN(xNum) && !isNaN(yNum);
                                      if (isNumber) {
                                          if ((dir === "asc" && xNum > yNum) || (dir === "desc" && xNum < yNum)) {
                                              shouldSwitch = true;
                                              break;
                                          }
                                      } else {
                                          if ((dir === "asc" && xContent.toLowerCase() > yContent.toLowerCase()) ||
                                              (dir === "desc" && xContent.toLowerCase() < yContent.toLowerCase())) {
                                              shouldSwitch = true;
                                              break;
                                          }
                                      }
                                  }
                                  if (shouldSwitch) {
                                      rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
                                      switching = true;
                                      switchcount++;
                                  } else {
                                      if (switchcount === 0 && dir === "asc") {
                                          dir = "desc";
                                          switching = true;
                                      }
                                  }
                              }
                          }
                      function toggleTheme() {
                              var table = document.getElementById("dockerStatus");
                              var isDark = document.getElementById("themeToggle").checked;
                              table.style.background = isDark ? "#1e1e1e" : "#ffffff";
                              table.style.color = isDark ? "#ffffff" : "#000000";
                              var th = table.getElementsByTagName("th");
                              for (var i = 0; i < th.length; i++) {
                                  th[i].style.background = isDark ? "#333" : "#dddddd";
                              }
                              localStorage.setItem("themeDark", isDark);
                          }
                          window.addEventListener("load", () => {
                              document.getElementById("themeToggle").checked = localStorage.getItem("themeDark") === "true";
                              toggleTheme();
                          });
                          }
                      </script>`;
                      return html;
                  }
                  
                  // Start und Zyklus
                  getTokenAndContainers();
                  schedule(`*/${updateIntervalMinutes} * * * *`, getTokenAndContainers);
                  
                  function buildMobileHTML(list) {
                      list.sort((a, b) => a.name.localeCompare(b.name));
                      let html = `<div style="font-family:Arial; font-size:14px;"><b>📱 Docker Übersicht</b><br><br>`;
                  
                      const columnWidth = mobileLayoutColumns === 2 ? '48%' : '100%';
                      const wrapperStyle = mobileLayoutColumns === 2 ? 'display:flex; flex-wrap:wrap; justify-content:space-between;' : '';
                  
                      html += `<div style="${wrapperStyle}">`;
                  
                      for (let c of list) {
                          const color = c.state === 'running' ? '#2d572c' : '#571b1b';
                          const cpu = parseFloat(c.cpu);
                          const mem = parseFloat(c.mem);
                          const statusText = mobileUseSymbol ? (c.state === 'running' ? '✅' : '❌') : c.state;
                          let metricsLine = '';
                          if (mobileShowCPU) metricsLine += ` CPU: ${cpu}%`;
                          if (mobileShowRAM) metricsLine += ` RAM: ${mem} MB`;
                  
                          let card = '';
                          if (mobileTwoLineLayout) {
                              card = `<div style="margin-bottom:12px; padding:8px; border-radius:8px; background:${color}; width:${columnWidth}; box-sizing:border-box;">
                                  <b>${c.name}</b><br>
                                  <small>${statusText}${metricsLine}</small><br>
                                  <small>Uptime: ${c.uptime}</small>
                              </div>`;
                          } else {
                              card = `<div style="margin-bottom:12px; padding:8px; border-radius:8px; background:${color}; width:${columnWidth}; box-sizing:border-box;">
                                  <b>${c.name}</b><br>
                                  <small>Status: ${statusText}${metricsLine}, Uptime: ${c.uptime}</small>
                              </div>`;
                          }
                          html += card;
                      }
                  
                      html += `</div></div>`;
                      return html;
                  }
                  
                  
                  

                  Edit2:
                  Screenshot von den Objects:
                  Screenshot 2025-08-01 at 14.38.20.png

                  Edit3:
                  Script um eine html Tabelle aus den States zu erzeugen, Datenpunkte und Container bitte anpassen:
                  wurde alles in ein Script gepackt, ist einfacher.

                  Screenshot: Tabelle
                  Screenshot 2025-08-01 at 16.15.07.png

                  Screenshot: Mobile Ansicht, 2 Spaltig (konfigurierbar)
                  Screenshot 2025-08-04 at 20.53.06.png

                  David G.D Online
                  David G.D Online
                  David G.
                  schrieb am zuletzt editiert von David G.
                  #12

                  @ilovegym

                  Die neue Version läuft.
                  Top.

                  Was ich mir wünschen würde:

                  • Discord deaktivieren zu können (hab es mir einfach unten rausgelöscht).
                  • Ggf Telegram hinzufügen (natürlich auch deaktivierbar)
                  • Created auch als TS (Da schaue ich, wann das letzte Update war um Container zu finden die nicht mehr gepflegt werden)

                  EDIT
                  Soll control die Container updaten?
                  Falls ja klappt es bei mir nicht.

                  EDIT 2
                  Wenn ich control in einem Container drücke, startet meine JS Instanz neu.

                  Zeigt eure Lovelace-Visualisierung klick
                  (Auch ideal um sich Anregungen zu holen)

                  Meine Tabellen für eure Visualisierung klick

                  ilovegymI 2 Antworten Letzte Antwort
                  1
                  • David G.D David G.

                    @ilovegym

                    Die neue Version läuft.
                    Top.

                    Was ich mir wünschen würde:

                    • Discord deaktivieren zu können (hab es mir einfach unten rausgelöscht).
                    • Ggf Telegram hinzufügen (natürlich auch deaktivierbar)
                    • Created auch als TS (Da schaue ich, wann das letzte Update war um Container zu finden die nicht mehr gepflegt werden)

                    EDIT
                    Soll control die Container updaten?
                    Falls ja klappt es bei mir nicht.

                    EDIT 2
                    Wenn ich control in einem Container drücke, startet meine JS Instanz neu.

                    ilovegymI Online
                    ilovegymI Online
                    ilovegym
                    schrieb am zuletzt editiert von
                    #13

                    @david-g

                    Control soll den Container starten / stoppen, das hat bei mir funktioniert..

                    Ich mach spätestens am Montag weiter.. 😃

                    Vielen Dank für das Testen!

                    1 Antwort Letzte Antwort
                    0
                    • David G.D David G.

                      @ilovegym

                      Die neue Version läuft.
                      Top.

                      Was ich mir wünschen würde:

                      • Discord deaktivieren zu können (hab es mir einfach unten rausgelöscht).
                      • Ggf Telegram hinzufügen (natürlich auch deaktivierbar)
                      • Created auch als TS (Da schaue ich, wann das letzte Update war um Container zu finden die nicht mehr gepflegt werden)

                      EDIT
                      Soll control die Container updaten?
                      Falls ja klappt es bei mir nicht.

                      EDIT 2
                      Wenn ich control in einem Container drücke, startet meine JS Instanz neu.

                      ilovegymI Online
                      ilovegymI Online
                      ilovegym
                      schrieb am zuletzt editiert von ilovegym
                      #14

                      @david-g

                      Hi David,
                      heute ist Montag und wie versprochen, eine neue Version, aktuell V3.3 - ist alles in ein Script gekommen und es gibt jede Menge neue Optionen, siehe Changelog :)

                      Viel Spass damit! (aktuelles Script im ersten Beitrag)

                      1 Antwort Letzte Antwort
                      0
                      Antworten
                      • In einem neuen Thema antworten
                      Anmelden zum Antworten
                      • Älteste zuerst
                      • Neuste zuerst
                      • Meiste Stimmen


                      Support us

                      ioBroker
                      Community Adapters
                      Donate

                      825

                      Online

                      32.4k

                      Benutzer

                      81.5k

                      Themen

                      1.3m

                      Beiträge
                      Community
                      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                      ioBroker Community 2014-2025
                      logo
                      • Anmelden

                      • Du hast noch kein Konto? Registrieren

                      • Anmelden oder registrieren, um zu suchen
                      • Erster Beitrag
                        Letzter Beitrag
                      0
                      • Home
                      • Aktuell
                      • Tags
                      • Ungelesen 0
                      • Kategorien
                      • Unreplied
                      • Beliebt
                      • GitHub
                      • Docu
                      • Hilfe