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.8k

script Portainer Api V3.3 - inkl. html Tabelle

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
blocklyjavascriptmonitoring
14 BeitrÀge 3 Kommentatoren 602 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 Online
    ilovegymI Online
    ilovegym
    schrieb am zuletzt editiert von ilovegym
    #4

    @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 1 Antwort Letzte Antwort
    0
    • 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

                        847

                        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