Skip to content
  • Home
  • Recent
  • Tags
  • 0 Unread 0
  • Categories
  • Unreplied
  • Popular
  • 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

  • Default (No Skin)
  • No Skin
Collapse
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Tester
  4. Neuer Adapter pi-hole2 für pihole>=V6

NEWS

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

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

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

Neuer Adapter pi-hole2 für pihole>=V6

Scheduled Pinned Locked Moved Tester
51 Posts 9 Posters 3.1k Views 13 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • OliverIOO OliverIO

    Neue Version 0.4.1

    • Das Problem mit der Farbgebung bei grünen Datenpunkten wurde behoben.
    SBorgS Offline
    SBorgS Offline
    SBorg
    Forum Testing Most Active
    wrote on last edited by
    #23

    @oliverio
    Danke, wollte die Tage schon die API per NodeRED Flow abfragen :blush:
    Pi-hole.png

    Richtet sich eher an den "Unbedarften":


    sendTo(
    "pi-hole2.0",
    "piholeapi",
    {
    method: "GET",
    endpoint: "/stats/summary"
    },
    function (data) {
    console.log(data.queries.percent_blocked);
    },
    );

    const json=getState('pi-hole2.0.Summary').val;
    const obj = JSON.parse(json);

    console.log(obj.queries.percent_blocked);


    Blockly.png

    Man sieht, man kommt auch so relativ einfach an die gewünschten Daten heran. Von daher sehe ich nicht unbedingt eine Notwendigkeit alles als Datenpunkt anbieten zu müssen.

    LG SBorg ( SBorg auf GitHub)
    Projekte: Lebensmittelwarnung.de | WLAN-Wetterstation | PimpMyStation

    OliverIOO 2 Replies Last reply
    0
    • SBorgS SBorg

      @oliverio
      Danke, wollte die Tage schon die API per NodeRED Flow abfragen :blush:
      Pi-hole.png

      Richtet sich eher an den "Unbedarften":


      sendTo(
      "pi-hole2.0",
      "piholeapi",
      {
      method: "GET",
      endpoint: "/stats/summary"
      },
      function (data) {
      console.log(data.queries.percent_blocked);
      },
      );

      const json=getState('pi-hole2.0.Summary').val;
      const obj = JSON.parse(json);

      console.log(obj.queries.percent_blocked);


      Blockly.png

      Man sieht, man kommt auch so relativ einfach an die gewünschten Daten heran. Von daher sehe ich nicht unbedingt eine Notwendigkeit alles als Datenpunkt anbieten zu müssen.

      OliverIOO Offline
      OliverIOO Offline
      OliverIO
      wrote on last edited by
      #24

      @sborg

      Wenn es eine hohe Nachfrage gibt, dann ist es kein Problem datenpunkte noch hinzuzunehmen.
      Daher auch meine Frage oben
      Bisher kam aber noch nicht soviel Anforderungen.
      Gut der Adapter ist jung und es ist Sommer.

      Summary wird halt im Standard jede Sekunde abgefragt und ich wollte im Standard jetzt nicht so hohe Last auf den iobroker legen.
      Gut den Abfrage Intervall kann man ggfs. auch in der Konfiguration senken, ich habe daher die Konfiguration auch etwas detaillierter aufgedröselt.

      Meine Adapter und Widgets
      TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
      Links im Profil

      1 Reply Last reply
      0
      • SBorgS SBorg

        @oliverio
        Danke, wollte die Tage schon die API per NodeRED Flow abfragen :blush:
        Pi-hole.png

        Richtet sich eher an den "Unbedarften":


        sendTo(
        "pi-hole2.0",
        "piholeapi",
        {
        method: "GET",
        endpoint: "/stats/summary"
        },
        function (data) {
        console.log(data.queries.percent_blocked);
        },
        );

        const json=getState('pi-hole2.0.Summary').val;
        const obj = JSON.parse(json);

        console.log(obj.queries.percent_blocked);


        Blockly.png

        Man sieht, man kommt auch so relativ einfach an die gewünschten Daten heran. Von daher sehe ich nicht unbedingt eine Notwendigkeit alles als Datenpunkt anbieten zu müssen.

        OliverIOO Offline
        OliverIOO Offline
        OliverIO
        wrote on last edited by
        #25

        @sborg

        Du hast nur 5% geblockt?
        Bei mir ist der Anteil bei über 50%
        Microsoft, Amazon,Samsung und Netflix telefonieren schon viel nach Hause.

        Meine Adapter und Widgets
        TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
        Links im Profil

        SBorgS 1 Reply Last reply
        0
        • OliverIOO OliverIO

          @sborg

          Du hast nur 5% geblockt?
          Bei mir ist der Anteil bei über 50%
          Microsoft, Amazon,Samsung und Netflix telefonieren schon viel nach Hause.

          SBorgS Offline
          SBorgS Offline
          SBorg
          Forum Testing Most Active
          wrote on last edited by
          #26

          @oliverio
          Meine Blocklist wurde offenbar nicht mehr geupdated. Habe ich aber nicht bemerkt, da ich fast nur mit meinem Laptop im Internet unterwegs bin. Da filtert der FF alles aus. Geladen habe ich den ganzen Kram natürlich dann aber doch, da ist das blocken per Pi-hole wesentlich effizienter. Dafür habe ich ihn ja auch.

          LG SBorg ( SBorg auf GitHub)
          Projekte: Lebensmittelwarnung.de | WLAN-Wetterstation | PimpMyStation

          1 Reply Last reply
          -1
          • OliverIOO Offline
            OliverIOO Offline
            OliverIO
            wrote on last edited by OliverIO
            #27

            Der Adapter müsste nun im beta auftauchen

            • Der erste Post wurde aufgrund dessen überarbeitet
            • Um eine fertige Version zu signalisieren wurde eine V1.0.0 released

            Meine Adapter und Widgets
            TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
            Links im Profil

            1 Reply Last reply
            1
            • OliverIOO Offline
              OliverIOO Offline
              OliverIO
              wrote on last edited by OliverIO
              #28

              Neue Version 1.1.0

              • Unter Data/Versions wurden Update Indikatoren hinzugefügt, um mittels true/false anzuzeigen, das ein Update für die einzelne Komponente vorliegt.

              Meine Adapter und Widgets
              TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
              Links im Profil

              L OliverIOO 2 Replies Last reply
              0
              • OliverIOO OliverIO

                Neue Version 1.1.0

                • Unter Data/Versions wurden Update Indikatoren hinzugefügt, um mittels true/false anzuzeigen, das ein Update für die einzelne Komponente vorliegt.
                L Offline
                L Offline
                Labersack
                wrote on last edited by Labersack
                #29

                @oliverio
                Habe den Adapter heute auch mal installiert.
                Was soll ich sagen... Ich finde, der Adapter ist ein Pi-Loch! lol
                Pi-Loch.jpg

                OliverIOO 1 Reply Last reply
                -1
                • L Labersack

                  @oliverio
                  Habe den Adapter heute auch mal installiert.
                  Was soll ich sagen... Ich finde, der Adapter ist ein Pi-Loch! lol
                  Pi-Loch.jpg

                  OliverIOO Offline
                  OliverIOO Offline
                  OliverIO
                  wrote on last edited by
                  #30

                  @labersack

                  Ja sowas, die automatische Übersetzung.

                  Danke fürs Bescheid geben.

                  Meine Adapter und Widgets
                  TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                  Links im Profil

                  1 Reply Last reply
                  0
                  • OliverIOO OliverIO

                    Neue Version 1.1.0

                    • Unter Data/Versions wurden Update Indikatoren hinzugefügt, um mittels true/false anzuzeigen, das ein Update für die einzelne Komponente vorliegt.
                    OliverIOO Offline
                    OliverIOO Offline
                    OliverIO
                    wrote on last edited by
                    #31

                    Neue Version 1.1.1

                    • Die Übersetzung wurde optimiert

                    Meine Adapter und Widgets
                    TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                    Links im Profil

                    S 1 Reply Last reply
                    0
                    • OliverIOO OliverIO

                      Neue Version 1.1.1

                      • Die Übersetzung wurde optimiert
                      S Offline
                      S Offline
                      sol21
                      wrote on last edited by
                      #32

                      @oliverio
                      Könnte man den Adapter in auf "stable" setzen?

                      Ich benutze normalerwiese auf nur die "stable" Versionen. Für den pi-hole2 habe ich bisher gezwungenermassen eine Ausnahme gemacht. Die 1.1.1 scheint ja recht gut zu laufen. Wenn man ihn nun auf "stable" setzt würde, würde er auch bei Leuten wie mir die nur das "stable"-Repository angewählt haben angezeigt.

                      PS: Aber grundsätzlich vielen Dank für die neue Version. Die hat mein IOBroker-PiHole Problem gelöst!

                      OliverIOO 1 Reply Last reply
                      0
                      • S sol21

                        @oliverio
                        Könnte man den Adapter in auf "stable" setzen?

                        Ich benutze normalerwiese auf nur die "stable" Versionen. Für den pi-hole2 habe ich bisher gezwungenermassen eine Ausnahme gemacht. Die 1.1.1 scheint ja recht gut zu laufen. Wenn man ihn nun auf "stable" setzt würde, würde er auch bei Leuten wie mir die nur das "stable"-Repository angewählt haben angezeigt.

                        PS: Aber grundsätzlich vielen Dank für die neue Version. Die hat mein IOBroker-PiHole Problem gelöst!

                        OliverIOO Offline
                        OliverIOO Offline
                        OliverIO
                        wrote on last edited by OliverIO
                        #33

                        @sol21
                        Normalerweise wird nach 15 Tagen automatisch ein issue eröffnet, aufgrund dessen ich dann die Versionen auf Stable setze.
                        Kam wohl diesmal nicht. Eventuell sind für manche Adapter die Regeln anderst oder es hat mit den github Problemen zu tun.

                        Meine Adapter und Widgets
                        TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                        Links im Profil

                        mcm1957M 1 Reply Last reply
                        0
                        • OliverIOO OliverIO

                          @sol21
                          Normalerweise wird nach 15 Tagen automatisch ein issue eröffnet, aufgrund dessen ich dann die Versionen auf Stable setze.
                          Kam wohl diesmal nicht. Eventuell sind für manche Adapter die Regeln anderst oder es hat mit den github Problemen zu tun.

                          mcm1957M Online
                          mcm1957M Online
                          mcm1957
                          wrote on last edited by mcm1957
                          #34

                          @oliverio
                          Nö 15 Tage Erinnering kommt nur bei existierenden Adapter u mind 5% der User spätestens aber nach 30 Tagen.

                          Die Regeln f eine Erinnerung sind immer gleich. :-)

                          An eine ERST Aufnahme wird erst nach 30 Tagen erinnert.

                          Kannst den Stable Request aber jederzeit stellen. Erinnerung dazu ist keine Erfordernis.

                          Entwicklung u Betreuung: envertech-pv, hoymiles-ms, ns-client, pid, snmp Adapter;
                          Support Repositoryverwaltung.

                          Wer Danke sagen will, kann nen Kaffee spendieren: https://paypal.me/mcm1957atiobroker

                          LESEN - gute Forenbeitrage

                          1 Reply Last reply
                          0
                          • OliverIOO Offline
                            OliverIOO Offline
                            OliverIO
                            wrote on last edited by OliverIO
                            #35

                            pi-hole2 v1.1.1 befindet sich nun im stable repository

                            Meine Adapter und Widgets
                            TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                            Links im Profil

                            1 Reply Last reply
                            1
                            • OliverIOO Offline
                              OliverIOO Offline
                              OliverIO
                              wrote on last edited by OliverIO
                              #36

                              Hier mal ein größerer Anwendungsfall für den pihole2 Adapter.
                              Bitte um Rückmeldungen, positive wie negative Erfahrungen.
                              Evtl baue ich das dann als widgets in den Adapter ein.

                              Bitte auch die Verwendung des Adapters vis-jsontemplate 4.1.3 weiter unten beachten
                              https://forum.iobroker.net/topic/31521/test-widget-json-template/32?_=1762189668521


                              Ich habe immer wieder den folgenden Anwendungsfall, der sich mit pihole Bordmitteln nicht so einfach lösen lässt.

                              Ich habe immer wieder mal Seiten, die aufgrund eines pihole Blocks nicht funktionieren. Darüber hinaus funken hier mehrere Geräte wirklich sehr oft nach Hause, deren requests durch pihole geblockt werden. Dies verhindert leider effizient die Suche nach den betroffenen domains des blocks, der wieder aufgehoben werden soll, da das logfile relativ voll ist.

                              Auch interessiert mich immer wieder mal, wer hier so nach Hause telefoniert.
                              Diejenigen domains, die ich aber schon mal geprüft habe will ich nicht erneut prüfen.

                              Daher nun der folgende Versuch einer Lösung mittels 2er Widgets die ich mit dem jsontemplate Adapter erstellt habe.

                              Widget 1

                              listet alle domains absteigend nach vorkommen über alle clients dar.
                              jede einzelne domain kann dann auf die Merkliste gesetzt werden.
                              die Liste kann nach einem Suchbegriff oder Regex gefiltert werden.
                              Mehrere Sichtbare domains können über ein Sammel-Checkbox ebenfalls in einem Schritt gemerkt werden.
                              Das widfget besteht 2 Reitern, einmal alle gemerkten domains, sowie alle die Nicht gemerkt worden sind.

                              2b733665-5606-4cb2-915a-a319c511ef3c-image.png

                              Die Konfiguration erfolgt im ersten Abschnitt des Templates

                              // config area
                              // adapter instance
                              const pihole_adapter_instance = 0; //Nummer der Instanz
                              const domain_count=200;  //maximale Anzahl der abzufragenden domains
                              const domain_blocked=true; //Abfrage der geblockten Domains im standard, Alternativ geht das auch mit den nicht geblockten domänen
                              

                              Das Template muss im entsprechenden Feld eingetragen werden. Zusätzlich muss noch ein Datenpunkt vom typ String angelegt werden und wie hier verknüpft werden.
                              Darin werden die bekannten domänen gespeichert.
                              9cf63a60-9a51-458d-b42a-c7a063d0b1aa-image.png

                              Template

                              <%
                              // config area
                              // adapter instance
                              const pihole_adapter_instance = 0;
                              const domain_count=200;
                              const domain_blocked=true;
                              %>
                              <%
                              //javascript code der per ejs interpretiert wird
                              const knownDP = Object.keys(dp)[0] || "";
                              const knownValue = dp[knownDP] || "[]";
                              const knownValueSet = new Set(JSON.parse(knownValue));
                              const adapterinstance = "pi-hole2."+pihole_adapter_instance;
                              //debugger;
                              const cookieKey = widgetID+"selectedTab";
                              const selectedTab = localStorage.getItem(cookieKey)||"tab-all";
                              
                              // Abruf der Daten vom pihole2 adapter
                              const apiresult = await getTopDomains(domain_count,domain_blocked); 
                              
                              // Aufbau Index
                              const domainCountMap = new Map((apiresult.domains || []).map(d => [d.domain, d.count]));
                              
                              // Aufbau der Tabellendaten knownDomains
                              const savedList = [...knownValueSet].map(domain => ({
                               domain,
                               count: domainCountMap.get(domain) || 0
                              }));
                              // Hilfsfunktion für async SendTo
                              async function sendToAsync(instance, command, sendData) {
                              return new Promise((resolve, reject) => {
                               try {
                                   vis.conn.sendTo(instance, command, sendData, function (receiveData) {
                                       resolve(receiveData);
                                   });
                               } catch (error) {
                                   reject(error);
                               }
                              });
                              }
                              //Hilfsfunktion Abruf der TopDomains blocked/not blocked 
                              async function getTopDomains(count,blocked) {
                              const blockedText = blocked ? "true":"false";
                              return await sendToAsync(adapterinstance,"piholeapi", {
                                 method: 'GET',
                                 endpoint: "/stats/top_domains?count="+count+"&blocked="+blockedText,
                              });
                              };
                              %>
                              
                              <style>
                              .pihole.select .tabs {
                                 display: flex;
                                 gap: .1rem;
                              }
                              
                              .pihole.select .tabs button {
                                 padding: .4rem .8rem;
                                 border: 1px solid #ccc;
                                 background: #f7f7f7;
                                 cursor: pointer;
                              }
                              
                              .pihole.select .tabs button.active {
                                 background: #e9eefc;
                                 border-color: #8aa3ff;
                              }
                              
                              .pihole.select .tabpanel {
                                 display: none;
                              }
                              
                              .pihole.select .tabpanel.active {
                                 display: block;
                              }
                              
                              .pihole.select table {
                                 width: 100%;
                                 border-collapse: collapse;
                              }
                              
                              .pihole.select th,
                              .pihole.select td {
                                 padding: .3rem .4rem;
                                 xborder-bottom: 1px solid #eee;
                              }
                              
                              .pihole.select .check {
                                 text-align: center;
                                 width: 10%;
                              }
                              .pihole.select .domain {
                                 text-align: left;
                                 xwidth: 70%;
                                 display: flex;
                                 align-items: center;
                                 gap: .4rem;            /* optional */	
                              }
                              .pihole.select .domain .filter-all {
                               flex: 1;               /* <-- restliche Breite */
                               min-width: 0;          /* wichtig für Firefox/Edge */
                               box-sizing: border-box;
                              }
                              
                              .pihole.select .count {
                                 text-align: end;
                                 width: 20%;
                              }
                              
                              .muted {
                                 color: #888;
                                 font-size: .9em;
                              }
                              .pihole.select th .filter {
                                 display:block; margin-top:.25rem;
                              }
                              .pihole.select th .filter input {
                                 width:100%; 
                                 box-sizing:border-box; 
                                 padding:.25rem .35rem;
                                 border:1px solid #ccc; 
                                 border-radius:4px; 
                                 font-size:.9rem;
                              }
                              .pihole.select th .filter small { 
                                 color:#666; 
                              }
                              </style>
                              
                              <div class="pihole select">
                               <div class="tabs" role="tablist">
                                 <button type="button" role="tab" aria-controls="tab-all" class="tabbtn <%= selectedTab=="tab-all"?"active":""%>">Alle Domains</button>
                                 <button type="button" role="tab" aria-controls="tab-saved" class="tabbtn <%= selectedTab=="tab-saved"?"active":""%>">Gemerkte Domains</button>
                               </div>
                              
                               <div id="tab-all" class="tabpanel <%= selectedTab=="tab-all"?"active":""%>" role="tabpanel" aria-label="Alle Domains">
                                 <table data-table="all">
                                   <thead>
                                     <tr>
                                       <th class="check">
                                         <span><input class="bulk" type="checkbox" data-action="check" checked></span>
                                       </th>
                                       <th class="domain">
                                         <span>domain</span>
                                         <input type="text" autofocus value="" class="filter-all">
                                       </th>
                                       <th class="count"><span>#</span></th>
                                     </tr>
                                   </thead>
                                   <tbody>
                                     <% (apiresult.domains || []).forEach(domain => { 
                                          if (!knownValueSet.has(domain.domain)) { %>
                                       <tr data-domain="<%= domain.domain %>">
                                         <td class="check">
                                           <input type="checkbox" data-id="<%= domain.domain %>">
                                         </td>
                                         <td class="domain"><%= domain.domain %></td>
                                         <td class="count"><%= domain.count %></td>
                                       </tr>
                                     <% } }) %>
                                   </tbody>
                                 </table>
                               </div>
                              
                               <div id="tab-saved" class="tabpanel <%= selectedTab=="tab-saved"?"active":""%>" role="tabpanel" aria-label="Gemerkte Domains">
                                 <table data-table="saved">
                                   <thead>
                                     <tr>
                                       <th class="check">
                                         <input class="bulk" type="checkbox" data-action="uncheck">
                                       </th>
                                       <th class="domain">
                                         <span>domain</span>
                                         <input type="text" autofocus value="" class="filter-all">
                                       </th>
                                       <th class="count">#</th>
                                     </tr>
                                   </thead>
                                   <tbody>
                                     <% savedList.forEach(item => { %>
                                       <tr data-domain="<%= item.domain %>">
                                         <td class="check">
                                           <input type="checkbox" data-id="<%= item.domain %>" checked>
                                         </td>
                                         <td class="domain">
                                           <%= item.domain %>
                                           <% if (!domainCountMap.has(item.domain)) { %>
                                             <span class="muted">(derzeit nicht in Liste)</span>
                                           <% } %>
                                         </td>
                                         <td class="count"><%= item.count %></td>
                                       </tr>
                                     <% }) %>
                                   </tbody>
                                 </table>
                               </div>
                              </div>
                              
                              <script>
                              //Javascript code der im browser läuft
                              (async () => {
                               // Funktionen für Tabs umschalten
                               const cookieKey = "<%= widgetID %>selectedTab";
                               const selectedTab = localStorage.getItem(cookieKey)||"tab-all";
                               const $tabs = $(".tabbtn");
                               const $panels = $(".tabpanel");
                               $tabs.on("click", function() {
                               //debugger;
                                 $tabs.removeClass("active");
                                 $(this).addClass("active");
                                 const id = $(this).attr("aria-controls");
                                 $panels.removeClass("active");
                                 $("#"+id).addClass("active");
                                 localStorage.setItem(cookieKey,id);
                               });
                              
                               // Daten aus den Datenpunkt wird ins Skriptn übernommen
                              //debugger;
                               const knownDP   = "<%- knownDP %>";
                               const initial   = JSON.parse(atob("<%= btoa(knownValue) %>"));
                               const knownSet  = new Set(initial);
                              
                               // Helpers
                               function writeDP() {
                                 //debugger;
                                 vis.setValue(knownDP, JSON.stringify(Array.from(knownSet)));
                               }
                               function makeRow(domain, count, checked=true) {
                                 return $(`
                                   <tr data-domain="${domain}">
                                     <td class="check"><input type="checkbox" data-id="${domain}" ${checked ? "checked":""}></td>
                                     <td class="domain">${domain}</td>
                                     <td class="count">${Number.isFinite(count) ? count : 0}</td>
                                   </tr>
                                 `);
                               }
                              
                               // Eventhandler Merken einer Domain bzw auch wieder entfernen
                               $('.pihole').on("change", 'tbody .check input', function(evt) {
                                 const id = $(evt.target).data("id");
                                 const isChecked = $(evt.target).is(":checked");
                                 const $row = $(evt.target).closest('tr');
                              
                                 //hinzufügen oder entfernen der gewählten informationen
                                 if (isChecked) {
                                   // nach "saved" verschieben
                                   knownSet.add(id);
                                   // Count aus aktueller Zeile holen
                                   const cnt = parseInt($row.find('.count').text(), 10) || 0;
                                   // In saved hinzufügen, falls noch nicht vorhanden
                                   if ($('[data-table="saved"] tr[data-domain="'+id+'"]').length === 0) {
                                     $('[data-table="saved"] tbody').append(makeRow(id, cnt, true));
                                   } else {
                                     // sicherstellen, dass dort gecheckt ist
                                     $('[data-table="saved"] tr[data-domain="'+id+'"] input[type="checkbox"]').prop('checked', true);
                                   }
                                   // Falls Änderung aus "all" kam: Zeile dort entfernen (weil nun gemerkt)
                                   if ($row.closest('table').attr('data-table') === 'all') {
                                     $row.remove();
                                   }
                                 } else {
                                   // -> zurück nach "all" verschieben
                                   knownSet.delete(id);
                                   // Count aus aktueller Zeile holen
                                   const cnt = parseInt($row.find('.count').text(), 10) || 0;
                                   // In all hinzufügen, falls noch nicht vorhanden
                                   if ($('[data-table="all"] tr[data-domain="'+id+'"]').length === 0) {
                                     $('[data-table="all"] tbody').append(makeRow(id, cnt, false));
                                   } else {
                                     // sicherstellen, dass dort ungecheckt ist
                                     $('[data-table="all"] tr[data-domain="'+id+'"] input[type="checkbox"]').prop('checked', false);
                                   }
                                   // Falls Änderung aus "saved" kam: Zeile dort entfernen
                                   if ($row.closest('table').attr('data-table') === 'saved') {
                                     $row.remove();
                                   }
                                 }
                                 writeDP();
                               });
                              
                               // Ereignishandler zum hinzufügen/entfernen aller sichtbaren Elemente
                               $('.pihole').on('click', 'thead .bulk', function() {
                               //debugger;
                                 const action = $(this).data('action'); // "check" | "uncheck"
                                 const $table = $(this).closest('table');
                                 const ids = $table.find('tbody tr:visible .check input').map((_,el)=>$(el).data('id')).get();
                                 //debugger;
                                 if (!ids.length) return;
                                 if (action === 'check') {
                                   ids.forEach(id => knownSet.add(id));
                                 } else {
                                   ids.forEach(id => knownSet.delete(id));
                                 }
                                 writeDP();
                               });
                               
                               // Domain Filter
                               const filterKey = "<%= widgetID %>filterAll";
                               const $filter   = $('.filter-all');
                               const $rowsAll  = $('[data-table="all"] tbody tr');
                               const $visCount = $('#all-visible-count');
                              
                               //Funktion zum filtern
                               function applyAllFilter(qRaw,table) {
                                 //debugger;
                                 const q = (qRaw || '').trim();
                                 let shown = 0;
                              
                                 // Modus erkennen: /regex/ oder Wildcards
                                 let isRegex = false, re = null;
                                 if (q.startsWith('/') && q.endsWith('/') && q.length > 2) {
                                     try {
                                         re = new RegExp(q.slice(1, -1), 'i'); 
                                         isRegex = true;
                                     } catch(e) { 
                                         /* fallback unten */ 
                                     }
                                 }
                                 if (!isRegex) {
                                   // * als Wildcard; rest escapen
                                   const esc = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\*/g, '.*');
                                   re = new RegExp(esc, 'i');
                                 }
                              
                                 $('[data-table="'+table+'"] tbody tr').each((_, tr) => {
                                   const dom = tr.getAttribute('data-domain') || tr.querySelector('.domain')?.textContent || '';
                                   const match = q === '' ? true : re.test(dom);
                                   tr.style.display = match ? '' : 'none';
                                   if (match) shown++;
                                 });
                              
                                 $visCount.text(shown);
                               }
                              
                               // Entprellen der Eingabe 
                               let fto;
                               $filter.on('input', function() {
                                 const v = this.value;
                                 clearTimeout(fto);
                                 const table = $(this.closest("table")).data("table");
                                 fto = setTimeout(() => {
                                   localStorage.setItem(filterKey, v);
                                   applyAllFilter(v,table);
                                 }, 120);
                               });
                              
                               // Beim Umschalten auf den Tab „Alle“ Filter erneut anwenden (falls DOM neu geändert wurde)
                               $('.tabbtn[aria-controls="tab-all"]').on('click', () => applyAllFilter($filter.val()));
                              
                               // Persistierten Filter laden & anwenden
                               const initialFilter = localStorage.getItem(filterKey) || "";
                               $filter.val(initialFilter);
                               applyAllFilter(initialFilter);
                               
                              })();
                              </script>
                              
                              

                              Widget2

                              Dieses Widget zeigt alle geblockten requests an. Auch hier können ein paar Konfigurationen vorgenommen werden.
                              Aufgrund dieser Lösung wurde noch ein kleiner glitch im jsontemplate entdeckt. Daher muss hier die Version 4.1.3 verwendet werden, ansonsten werden im 2.widget die Liste nicht korrekt dargestellt.

                              d6c55515-a712-475f-80b8-2f417be28632-image.png
                              Das Template muss im entsprechenden Feld eingetragen werden. Zusätzlich muss noch ein Datenpunkt vom typ String angelegt werden und wie hier verknüpft werden.
                              Darin werden die bekannten domänen gespeichert.

                              <%
                              // config area
                              // adapter instance
                              const pihole_adapter_instance = 0;  //Adapter Instanz
                              const blocked = true;  //Wie oben, ob geblockte oder nicht geblockte requests abgerufen werden soll
                              const request_count=500;  // Anzahl der letzten requests, also ab dem jetzigen Zeitpunkt die letzten 500 requests.
                              %>
                              

                              943fa6bf-e513-494e-a836-249b47749fb1-image.png

                              Template

                              <%
                              // config area
                              // adapter instance
                              const pihole_adapter_instance = 0;
                              const blocked = true;
                              const request_count=500;
                              %>
                              <%
                              const knownDP = Object.keys(dp)[0] || "";
                              const knownValue = dp[knownDP] || "[]";
                              %>
                              
                              <style>
                              .pihole.requests {
                                 font-size: 0.8em;
                              }
                              .pihole.requests .col {
                                 text-align: left;
                              }
                              .pihole.requests .col.time {
                                 width: 15%;
                              }
                              .pihole.requests .col.status {
                                 width: 5%;
                              }
                              .pihole.requests .col.type {
                                 width: 5%;
                              }
                              .pihole.requests .col.domain {
                                 width: 20%;
                              }
                              .pihole.requests .col.client_name {
                                 width: 20%;
                              }
                              .pihole.requests .col.client_ip {
                                 width: 10%;
                              }
                              
                              </style>
                              <div class="pihole requests">
                                 <div>
                                     <select class="clientsb" name="clients">
                                           <option value="">no Clients</option>
                                     </select>
                                 </div>
                                 <table data-table="all">
                                 	<thead>
                                 		<tr>
                                 			<th class="col time">Time</th>
                                 			<th class="col status">Status</th>
                                 			<th class="col type">Type</th>
                                 			<th class="col domain">Domain</th>
                                 			<th class="col client_name">Client Name</th>
                                 			<th class="col client_ip">Client IP</th>
                                 		</tr>
                                 	</thead>
                                 	<tbody>
                                     </tbody>
                                 </table>
                              </div>
                              <script>
                              //Javascript code der im browser läuft
                              (async () => {
                                 const pihole_adapter_instance = 0;
                                 const adapterinstance = "pi-hole2."+pihole_adapter_instance;
                                 const blocked = <%- blocked %>;
                                 const request_count=<%- request_count %>;
                              
                                 const knownDP   = "<%- knownDP %>";
                                 const knownValue = atob("<%= btoa(knownValue) %>");
                                 const knownValueSet = new Set(JSON.parse(knownValue));
                              
                                 let fto;    
                                 let $table=$(".pihole.requests tbody");
                              
                                 await fillClients();
                              
                                 function fillTable() {
                                     let table=$(".pihole.requests tbody"); 
                                 }
                                 async function fillClients() {
                              
                                   const key = "<%= widgetID %>selectedclient";
                                   const selectedClient = localStorage.getItem(key)||"";
                                 
                                     let clients = await getClients();
                                     let $clientselect = $(".pihole.requests .clientsb");
                              
                                     const clientlist = Object.entries(clients.clients)
                                       .map(([ip, v]) => ({
                                         ip, ...v
                                       }))
                                       .sort((a, b) => b.total - a.total);
                                     $clientselect.empty();
                                     $clientselect.append($('<option value="" '+isSelected(selectedClient,"")+'>nothing selected</option>'));
                                     clientlist.map(el=>$clientselect.append($('<option value="'+el.ip+'" '+isSelected(selectedClient,el.ip)+'>'+el.name+'/'+el.ip+'</option>')));
                                     let that=this;
                                     $clientselect.on('change', function() {
                                       const v = this.value;
                                       clearTimeout(fto);
                                       fto = setTimeout(() => {
                                         localStorage.setItem(key, v);
                                         applySelect(v);
                                       }, 120);
                                     });
                              
                                     if (selectedClient) {
                                         applySelect(selectedClient);
                                     }
                                     async function applySelect(client) {
                                         let requests =  await getFilteredRequests(client /*"192.168.1.232"*/,request_count);
                                         $table.empty();
                                         requests.queries
                                           .filter(req=>!knownValueSet.has(req.domain))
                                           .map(req=>$table.append(makeRow(req)));
                                     }
                                 }
                                 function findIPv4(adresses) {
                                     let ips = adresses||[];
                                 
                                     return (adresses||"").split(",").filter(ip=>ip.split(".").length-1)[0]||"";
                                 }
                                 function isSelected(v1,v2) {
                                     return (v1==v2) ? "selected":"";
                                 }
                                 function makeRow(item) {
                                     return $(`
                                         <tr>
                                             <td class="col time">`+         formatDate(item.time)+`</th>
                                             <td class="col status">`+       item.status+`</th>
                                             <td class="col type">`+         item.type+`</th>
                                             <td class="col domain">`+       item.domain+`</th>
                                             <td class="col client_name">`+  item.client.name+`</th>
                                             <td class="col client_ip">`+    item.client.ip+`</th>
                                         </tr>
                                     `);
                                 }
                                 async function getData(client) {
                                     if (!client) {
                                         client = clients[0];
                                     }
                                     return await getFilteredRequests(client /*"192.168.1.232"*/,request_count);
                                 }
                                 async function sendToAsync(instance, command, sendData) {
                                     return new Promise((resolve, reject) => {
                                         try {
                                             vis.conn.sendTo(instance, command, sendData, function (receiveData) {
                                             resolve(receiveData);
                                         });
                                         } catch (error) {
                                             reject(error);
                                         }
                                     });
                                 }
                                 async function getClients() {
                                     return await sendToAsync(adapterinstance,"piholeapi", {
                                         method: 'GET',
                                         endpoint: "/history/clients?N=200",
                                     });
                                 };
                                 async function getFilteredRequests(client_ip,count) {
                                     return await sendToAsync(adapterinstance,"piholeapi", {
                                         method: 'GET',
                                         endpoint: "/queries?client_ip="+client_ip+"&upstream=blocklist&order%5B0%5D%5Bdir%5D=desc&start=0&length="+count,
                                     });
                                 };
                                 function formatDate(ts) {
                                     const date = new Date(ts * 1000);
                                     return date.toISOString();
                                 }
                              
                              })();
                              </script>
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              

                              Meine Adapter und Widgets
                              TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                              Links im Profil

                              1 Reply Last reply
                              0
                              • MartinPM Online
                                MartinPM Online
                                MartinP
                                wrote on last edited by
                                #37

                                Mein Sohn beschwert sich, dass er bei der Google Suche die Werbe-Links oben NICHT klicken kann.
                                Wenn er nach Auto-Teilen sucht, ist die Werbung doch manchmal nützlich.

                                Ansonsten findet er das Blocken durchaus nützlich.
                                Gäbe es die Möglichkeit für feinere Abstufungen des Abschaltens des Blockings?

                                • Blocking nur für ein/mehrere Client-Device (sein Notebook, sein Gaming-PC, sein Smartphone) temporär generell aufheben
                                • Temporär ein Profil aktivieren, was die Google-Adlinks generell durchlässt

                                Dann könnte ich ihm z. B. einen Vis2 View basteln, wo er das mit einem Fingertipp aktivieren kann ...

                                Intel(R) Celeron(R) CPU N3000 @ 1.04GHz 8G RAM 480G SSD
                                Virtualization : unprivileged lxc container (debian 12 on Proxmox 8.4.14)
                                Linux pve 6.8.12-16-pve
                                6 GByte RAM für den Container
                                Fritzbox 6591 FW 8.03 (Vodafone Leih-Box)
                                Remote-Access über Wireguard der Fritzbox

                                OliverIOO BananaJoeB 2 Replies Last reply
                                0
                                • mcm1957M Online
                                  mcm1957M Online
                                  mcm1957
                                  wrote on last edited by mcm1957
                                  #38

                                  Die Filterung wird durch pi-hole bestimmt - und nicht durch den Adapter.
                                  Schau dir mal die pi-hole Dokumentation an um zu sehen was du da wie einstellen kannst.

                                  Oder ziehlt die Frage darauf ab dass das Widget weitere Funktionen von pi-hole unterstützen soll?

                                  Entwicklung u Betreuung: envertech-pv, hoymiles-ms, ns-client, pid, snmp Adapter;
                                  Support Repositoryverwaltung.

                                  Wer Danke sagen will, kann nen Kaffee spendieren: https://paypal.me/mcm1957atiobroker

                                  LESEN - gute Forenbeitrage

                                  1 Reply Last reply
                                  0
                                  • MartinPM MartinP

                                    Mein Sohn beschwert sich, dass er bei der Google Suche die Werbe-Links oben NICHT klicken kann.
                                    Wenn er nach Auto-Teilen sucht, ist die Werbung doch manchmal nützlich.

                                    Ansonsten findet er das Blocken durchaus nützlich.
                                    Gäbe es die Möglichkeit für feinere Abstufungen des Abschaltens des Blockings?

                                    • Blocking nur für ein/mehrere Client-Device (sein Notebook, sein Gaming-PC, sein Smartphone) temporär generell aufheben
                                    • Temporär ein Profil aktivieren, was die Google-Adlinks generell durchlässt

                                    Dann könnte ich ihm z. B. einen Vis2 View basteln, wo er das mit einem Fingertipp aktivieren kann ...

                                    OliverIOO Offline
                                    OliverIOO Offline
                                    OliverIO
                                    wrote on last edited by
                                    #39

                                    @MartinP

                                    Wenn du mir sagst wie man das über die pi hole gui macht könnte ich schauen wie man das über die api abbilden könnte.
                                    So detailliert kenne ich pihole nicht.
                                    Hier reichen uns die einheitlichen Möglichkeiten.

                                    Meine Adapter und Widgets
                                    TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                                    Links im Profil

                                    1 Reply Last reply
                                    1
                                    • MartinPM Online
                                      MartinPM Online
                                      MartinP
                                      wrote on last edited by
                                      #40

                                      Muss ich mir anschauen, die API habe ich noch nicht genutzt, habe die Basteleien bisher in der Web-Oberfläche von Pi-Hole gebaut ...

                                      Möglicherweise muss ich da auch noch einmal von vorne anfangen, und PiHole komplett neu aufsetzen..

                                      Läuft derzeit in einer sehr indirekten Installation Proxmox -> LXC-Container -> Docker ->PiHole

                                      Solle MACVLAN benutzen, sonst könne man gar keine Randscharfe Zuordnung zu Clients gewährleisten.

                                      Intel(R) Celeron(R) CPU N3000 @ 1.04GHz 8G RAM 480G SSD
                                      Virtualization : unprivileged lxc container (debian 12 on Proxmox 8.4.14)
                                      Linux pve 6.8.12-16-pve
                                      6 GByte RAM für den Container
                                      Fritzbox 6591 FW 8.03 (Vodafone Leih-Box)
                                      Remote-Access über Wireguard der Fritzbox

                                      OliverIOO 1 Reply Last reply
                                      0
                                      • MartinPM MartinP

                                        Muss ich mir anschauen, die API habe ich noch nicht genutzt, habe die Basteleien bisher in der Web-Oberfläche von Pi-Hole gebaut ...

                                        Möglicherweise muss ich da auch noch einmal von vorne anfangen, und PiHole komplett neu aufsetzen..

                                        Läuft derzeit in einer sehr indirekten Installation Proxmox -> LXC-Container -> Docker ->PiHole

                                        Solle MACVLAN benutzen, sonst könne man gar keine Randscharfe Zuordnung zu Clients gewährleisten.

                                        OliverIOO Offline
                                        OliverIOO Offline
                                        OliverIO
                                        wrote on last edited by OliverIO
                                        #41

                                        @MartinP

                                        nicht du sollst dich mit der api beschäftigen.
                                        du sollst nur schauen wie du dein ziel mit der web oberfläche (=gui, graphical user interface) umsetzen kannst.

                                        wenn du das beschreiben kannst, kann man schauen was man damit im adapter machen kann.

                                        für macvlan musst du eigentlich nicht nneu installieren.
                                        das ist reine docker konfiguration.
                                        wie das mit Proxmox/LXC aussieht weiß ich allerdings nicht, da dann der Netzwerkverkehr nochmal durch eine weitere schicht durchgeführt wird.

                                        ich gehe mal davon aus, das man in pihole irgendwas mit client zu gruppen zuordnung machen muss und dann clients dynamisch zu gruppen zuordnet oder wieder rausnimmt um dann blocking ein/auszuschalten

                                        Meine Adapter und Widgets
                                        TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
                                        Links im Profil

                                        1 Reply Last reply
                                        0
                                        • MartinPM MartinP

                                          Mein Sohn beschwert sich, dass er bei der Google Suche die Werbe-Links oben NICHT klicken kann.
                                          Wenn er nach Auto-Teilen sucht, ist die Werbung doch manchmal nützlich.

                                          Ansonsten findet er das Blocken durchaus nützlich.
                                          Gäbe es die Möglichkeit für feinere Abstufungen des Abschaltens des Blockings?

                                          • Blocking nur für ein/mehrere Client-Device (sein Notebook, sein Gaming-PC, sein Smartphone) temporär generell aufheben
                                          • Temporär ein Profil aktivieren, was die Google-Adlinks generell durchlässt

                                          Dann könnte ich ihm z. B. einen Vis2 View basteln, wo er das mit einem Fingertipp aktivieren kann ...

                                          BananaJoeB Online
                                          BananaJoeB Online
                                          BananaJoe
                                          Most Active
                                          wrote on last edited by
                                          #42

                                          @MartinP sagte in Neuer Adapter pi-hole2 für pihole>=V6:

                                          Blocking nur für ein/mehrere Client-Device (sein Notebook, sein Gaming-PC, sein Smartphone) temporär generell aufheben
                                          Temporär ein Profil aktivieren, was die Google-Adlinks generell durchlässt

                                          Naja, Die Möglichkeit temporär Pi-Hole zu deaktivieren (für x Minuten oder Stunden) ist in der GUI eingebaut. Ob das über die API geht ... vermutlich.

                                          Hier hat einer ein Bash-Skript gebaut welches Gruppen per API aktiviert oder deaktiviert:
                                          https://gist.github.com/Data-Monkey/d88199a51feb715f7d67a47900c846bb
                                          Ein Client kann ja in mehreren Gruppen sein, wenn der also zusätzlich in einer Gruppe ist bei der nicht gefiltert ist, könnte man das über diesen Weg ein- und ausschalten (vermutlich)

                                          ioBroker@Ubuntu 24.04 LTS (VMware) für: >260 Geräte, 5 Switche, 7 AP, 9 IP-Cam, 1 NAS 42TB, 1 ESXi 15TB, 4 Proxmox 1TB, 1 Hyper-V 48TB, 14 x Echo, 5x FireTV, 5 x Tablett/Handy VIS || >=160 Tasmota/Shelly || >=95 ZigBee || PV 8.1kW / Akku 14kWh || 2x USV 750W kaskadiert || Creality CR-10 SE 3D-Drucker

                                          OliverIOO 2 Replies Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          874

                                          Online

                                          32.4k

                                          Users

                                          81.5k

                                          Topics

                                          1.3m

                                          Posts
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Home
                                          • Recent
                                          • Tags
                                          • Unread 0
                                          • Categories
                                          • Unreplied
                                          • Popular
                                          • GitHub
                                          • Docu
                                          • Hilfe