Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. OliverIO

    NEWS

    • Wir empfehlen: Node.js 22.x

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Profile
    • Following 1
    • Followers 10
    • Topics 61
    • Posts 7554
    • Best 1244
    • Groups 4

    OliverIO

    @OliverIO

    Meine Adapter und Widgets:
    TVProgram Github Forum
    Squeezebox Github Forum
    OpenLiga Github Forum
    RSSFeed Github Forum
    MyTime Github Forum

    1528
    Reputation
    1037
    Profile views
    7554
    Posts
    10
    Followers
    1
    Following
    Joined Last Online

    OliverIO Follow
    Developer Pro Starter Most Active

    Best posts made by OliverIO

    • Test Adapter tvprogram
      Aktuelle Test Version 0.0.1 und größer
      Veröffentlichungsdatum Januar 2021
      Github Link https://github.com/oweitman/iobroker.tvprogram

      Was kann der Adapter bereits:
      Server

      • die notwendigen TV-Daten aus dem Internet abrufen und als Datenpunkt abspeichern

      Widget
      -Darstellung des TV-Programms in der Zeitstrahl-Ansicht
      -Aufruf von Detailinformationen zu jeder einzelnen Sendung, soweit die Daten vorhanden sind.

      • Scrollen über einen ganzen Tag
      • Senderauswahl konfigurieren

      Was kann das Widget noch nicht:

      • Konfiguration der Darstellung, größerer/kleinerer Bereich je Stunde, ggfs auch hinein/hinauszoomen
      • Farbanpassung an das gewünschte Farbschema, aktuell nur Dunkel-Darstellung
      • Hervorhebung Favoritensendungen

      Weitere Punkte auf der Roadmap

      • Ideen für weitere Widgets auf Basis des bereits existierenden TVprogram-Skripts
      • sendungserinnerungen
      • ansteuern datenpunkt zum umschalten nach Klick auf senderlogo
      • Datenadapter für weitere Quellen (Internet, Hardware wie Enigma,VU-Box)

      Darüber hinaus wird in diesem Thread weiter unten immer aktuell über den Fortschritt berichtet

      Hier ein kleines Video des bisherigen Entwicklungsstandes.
      tvprogram.gif

      posted in Tester
      OliverIO
      OliverIO
    • Test/Support für Adapter rssfeed und vis-2-widgets-rssfeed

      Unter Abteilung Tester konnte ich kein Topic anlegen. Bitte dort hin verschieben.

      Nachdem ich immer weider lese, wie sich die Leute mit rss-Feeds abmühen,
      habe ich einen Adapter dazu geschrieben.
      Dieser hat aktuell minimale Fähigkeiten, man kann damit aber
      schon annehmbare Ergebnisse erzielen.

      Verwendete Bausteine
      Um die Flexibilität bei der Anzeige der Feeds bieten zu können, erfolgt die Formatierung der Ausgabe
      über ein template-system.
      Eine Vorlage werde ich hier weiter unten kopieren.
      Beschreibung der Formatierung und Syntax finden sich auf den folgenden Seiten
      https://ejs.co/ und https://github.com/mde/ejs

      Einlesen der Feeds.
      Dazu verwende ich ein node modul namens feedparser.
      Dieses Modul kann verschiedene RSS-Standards (RSS, Atom, and RDF).
      Dazu ist er noch dazu in der Lage, die Attribute zu normalisieren, so das es in der Ausgabe egal ist,
      wie der jeweilige Standard ein bestimmtes Feld nennt.
      https://github.com/danmactough/node-feedparser

      Installation und Einrichtung

      Schritt 1 - Installation
      Der Adapter ist aktuell nur auf github verfügbar.
      Name des Repository ist https://github.com/oweitman/iobroker.rssfeed

      Schritt 2 - Instanz hinzufügen
      Der Adapter müsste dann im Abschnitt adapter im iobroker angezeigt werden.
      Manchmal kommt es vor, das insbesondere bei Webänderungen (Widgets/Konfigurationsdialog) die Änderungen nicht sichtbar sind, muss evtl. auf der Kommandozeile folgender Befehl ausgeführt werden:
      iobroker upload rssfeed
      Im rechten Bereich in der Zeile des Adapters kann über den Plus-Knopf eine Instanz hinzugefügt werden

      Schritt 3 - Konfiguration

      Die Konfiguration ist relativ simpel. Es gibt nur wenige Felder

      Refresh: ist die generelle Vorgabe, wie oft in Minuten der Feed neu abgerufen werden soll. Voreinstellung ist 60
      Maximale Artikel im Datenpunkt: Hier kann die Gesamtmenge der zu verarbeitenden Daten begrenz werden.

      Dann je neuen Feed:
      Name: Ein eindeutiger Name, darf nicht doppelt vorkommen
      Url: Die vollständige Adresse des Feed (mit http:// oder https://)
      Refresh: Bei Erfassung kann ein abweichender Wert angegeben werden. Ansonsten wird die generelle Vorgabe genommen

      Nach Neustart und Abruf der Daten ist der Feed als JSON-Datenpunkt im Objektbaum zu finden.

      Schritt 4 - vis und widgets

      Um die Feeds anzuzeigen gibt es ein widget. Dieses kann über den Suchfilter in vis mittels rssfeed gefunden werden.

      Das widget hat folgende Einstellmöglichkeiten

      rss_oid Hier wird der JSON-Datenpunkt des gewünschten feeds ausgewählt. Ich habe festgestellt, das der Objektbrowser hier nicht imnmer zufriedenstellend funktioniert, da er die im JSON enthaltenen HTML-Anteile versucht dazustellen.
      Alternativ die Datenpunkt ID direkt aus vis kopieren.
      template: Hier kann ein template erfasst werden, welches javascript und html gemischt enthalten kann.

      maxarticles: Hier kann widget individuell die Anzahl der Artikel begrenzt werden.

      Alle anderen Einstellungen sind identisch zu den anderen widgets, Die Formatvorgaben gellten generell für alle widgetinhalte

      Die widgets rssfeed Meta helper und rssfeed Article helper unterstützen bei der Erstellung eines Templates,
      indem dort komfortabel die meta-Informationen und die Attribute eines Artikels angezeigt werden.
      Im widget rssfeed Article Helper kann man den Artikel auswählen, dessen Attribute angezeigt werden soll, sowie das Präfix anpassen, so dass es einfacher ist dieses in das Template zu kopieren.

      Ein weiteres widget hat nichts direkt mir rssfeed zu tun und ist in diesem adapter voraussichtlich temporär zu Gast.

      Template anhand von Beispielen
      Ein Beispiel, welches ich mit den folgenden RSS Feeds getestet habe:

      • http://www.tagesschau.de/xml/rss2
      • https://www.bild.de/rssfeeds/rss3-20745882,feed=alles.bild.html
      <%= meta.title %> 
      <% articles.forEach(function(item){ %>
      <p><small><%- vis.formatDate(item.pubdate, "TT.MM.JJJJ SS:mm") %></small></p>
      <h3><%- item.title %></h3>
      <p><%- item.description %></p>
      <div style="clear:both;" />
      <% }); %>
      

      Das Templatesystem arbeitet mit bestimmten Tags.
      Die verwendeten Tags bedeuten das folgenden
      <%= Der Inhalt des enthaltenen Ausdrucks/Variable wird escaped ausgegenen.
      <%- Der Inhalt des enthaltenen Ausdrucks/Variable wird unescaped ausgegenen.
      <% Tag ohne Ausgabe, wird für javascriptanweisungen verwendet
      %> ist generell ein schließender Tag um eines der vorherigen abzuschließen
      Alles was außerhalb dieser Tags ist, wird genau so dann angezeigt bzw. wenn es HTML ist als HTML interpretiert ausgegeben. (siehe bspw das p-tag,div-tag,small-tag

      Innerhalb des Templates habt ihr 2 vorgegebene variablen zur Verfügung

      meta: Hier sind alle Metainformationen zum Feed enthalten. Die folgenden Inhalte stehen zur Verfügung. Ich denke die Bezeichner sind selbst erklärend. In der Hilfe werde ich diese noch genauer Beschreiben. bzw den Inhalt spezifizieren (manche sind Arrays)

      meta.title
      meta.description
      meta.link
      meta.xmlurl
      meta.date
      meta.pubdate
      meta.author
      meta.language
      meta.image
      meta.favicon
      meta.copyright
      meta.generator
      meta.categories

      articles: Ist ein Array mit einzelnen Elementen (javascript array). Jedes Element hat die folgenden Eigenschaften.
      Damit es zum Beispiel passt mache ich hier mal das Prefix item davor. Aber wenn ihr wollt könnt ihr das selbst wählen. Es muss dann nur in der Schleife (forEach) entsprechend benannt werden. Auch hjier sind die Bezeichner erst einmal selbst erklärend. Nicht in jedem Feed sind alle attribute auch gefüllt. Die wichtigsten sind im obigen Template bereits enthalten.

      item.title
      item.description
      item.summary
      item.link
      item.origlink
      item.permalink
      item.date
      item.pubdate
      item.author
      item.guid
      item.comments
      item.image
      item.categories
      item.source
      item.enclosures"

      <%= meta.title %> 
      <% articles.forEach(function(item){ %>
      <p><small><%- vis.formatDate(item.pubdate, "TT.MM.JJJJ SS:mm") %></small></p>
      <h3><%- item.title %></h3>
      <p><%- item.description %></p>
      <div style="clear:both;" />
      <% }); %>
      

      Kurze Beschreibung was in den einzelnen Zeilen passiert
      Z1: Der Feed-titel wird ausgegeben
      Z2: Ohne Ausgabe. Schleife über alle articles, bei jedem Durchgang wird das aktuelle Element der variable item zugewiesen.
      Z3: Datum und Uhrzeit wird ausgegeben und wird in einem p/small-Tag eingeschlossen zur Formatierung. Zur Formatierung wird die vis-eigene Datumsformatfunktion verwendet. Beschreibung ist im adapter vis zu finden.
      Z4: Der Titel des Artikels wird ausgegeben. Zur Formatierung wird der Tag für Überschrift 3 verwendet
      Z5: Der Inhalt des Artikels wird ausgegeben und in einem p-Tag eingeschlossen. Hier ist, zumindest bei den beiden Beispielen, HTML-code enthalten, der meist ein Bild, sowie beschreibenden Text mitbringt
      Z6: Ein div-Tag, das ausgegeben werden muss, um eine spezielle Formatierung im Feed wieder aufzuheben.
      Z7: Ohne Ausgabe. Hier wird die Schleife geschlossen. Alles was zwischen Z2 und Z7 definiert wurde, wird für jeden einzelnen Artikel wiederholt.

      Ich freue mich über reges testen und Vorschlag von Erweiterungen.

      Fehler können hier, aber auch auf github https://github.com/oweitman/ioBroker.rssfeed
      gemeldet werden.

      posted in Tester
      OliverIO
      OliverIO
    • Test Adapter OpenLigaDB

      Unter Abteilung Tester konnte ich kein Topic anlegen. Bitte dort hin verschieben.

      ich bitte um Test eines neuen Adapters zur Anzeige von
      Sportergebnissen und Spielinformationen von OpenLigaDB.

      Installation und Einrichtung

      Schritt 1 - Installation
      Der Adapter ist unter OpenLigaDB im Latest-Repository verfügbar und kann normal werden.

      Schritt 2 - Instanz hinzufügen

      • Der Adapter müsste dann im Abschnitt adapter im iobroker angezeigt werden.
        Manchmal kommt es vor, das insbesondere bei Webänderungen (Widgets/Konfigurationsdialog) die Änderungen nicht sichtbar sind, muss evtl. auf der Kommandozeile folgender Befehl ausgeführt werden:
      iobroker upload openligadb
      
      • Im rechten Bereich in der Zeile des Adapters kann über den Plus-Knopf eine Instanz hinzugefügt werden

      Schritt 3 - Konfiguration

      • Im Abschnitt Instanzen im iobroker müsste dann die erzeugte Instanz angezeigt werden

      • Über das Schraubenschlüsselsymbol kommt man in die Konfiguration

      • Dort müssen die verschiedenen Ligen und Saisons erfasst werden.

      • Das jeweilige Kürzel (Shortcut) kann auf der Seite openligadb.de nachgeschaut werden.

      • Die Saison ist bspw bei Fußball immer das Startjahr.

      • Beide Informationen müssen exakt so eingegeben werden, wie dort angezeigt.
        Bspw zum Test:
        Aktuelle 1. Bundesliga, Kürzel bl1 und Saison 2019
        Aktuelle 2. Bundesliga, Kürzel bl2 und Saison 2019

      • Der Entwicklertest ist genau mit diesen Ligen erfolgt. andere Ligen habe ich bisher noch nicht ausprobiert.

      • Nach Erfassung und speichern der Konfiguration wird der Adapter neu gestartet und für jede Liga werden verschiedene Datenpunkte mit JSON-Informationen angelegt und regelmäßig (refresh) aktualisiert.

      weitere durch Tester bereits verwendete Ligen sind bl2,bl3 und cl1920german die für
      Saison 2019 verfügbar sind.

      Schritt 4 - vis und widgets

      • Aktuell habe ich 3 widgets gebaut
      • Diese können gefunden werden, wenn man im widget-Filter openligadb auswählt/eingibt

      Table zeigt den aktuellen Tabellenstand an.

      • Nach dem Hinzufügen des Widgets in einer view, muss der entsprechende Datenpunkt ausgewählt werden (Datenpunkbezeichnung table in dem jeweiligen Liga/Season-Unterverzeichnis)
      • Die Schrift kann über die bekannten CSS-Einstellungen im rechten Bereich vorgenommen werden.
      • Über die Eigenschaft maxicon kann die Icongröße des Mannschaftslogos unabhängig eingestellt werden. maximale Pixel in horizontal wie auch waagerecht
      • Manchmal (Nicht immer) ist ein Kurzname für die Manschaft gefplegt. der kann über shortname gewählt werden

      Gameday zeigt den aktuellen Spieltag mit Datum/Uhrzeit und Ergebnissen an.

      • Vorgehensweise ist ähnlich wie bei Table.
      • Auswahl des Datenpunktes (hier heißt er currgameday)
      • Die restlichen Einstellungen sind identisch zu Table
      • Dazu am besten in die Widget-Hilfe in vis gehen.

      FavGame Anzeige der Zeiten Deiner Lieblingsmannschaften

      • Einstellungen sind fast analog zu GameDay
      • Dazu am besten in die Widget-Hilfe in vis gehen.
        10b3b7fb-627a-45f6-966e-14a15189fbb1-image.png

      Ich freue mich über reges testen und Vorschlag von Erweiterungen.
      Testet bitte auch mal die anderen Ligen von anderen Sportarten.
      Allerdings weiß ich nicht wie vollständig diese gepflegt sind. Die guten sind wohl mit einem gelben Stern (Top-Liga) markiert.

      Fehler können hier, aber auch auf github https://github.com/oweitman/ioBroker.openligadb
      gemeldet werden.

      posted in Tester
      OliverIO
      OliverIO
    • fail2ban gui

      Ich setze für meinen reverse proxy zur zusätzlichen Absicherung fail2ban ein.

      fail2ban kann log-files (bei mir primär von nginx und nextcloud) live scannen und je nach Mustererkennung (bei mir hauptsächlich auftretende Fehler) IP-Adressen nach einer gewissen Anzahl von Fehlversuchen per firewall bannen.

      Da es da leider keine einfache GUI gibt und die Kommandozeile mir zu unhandlich ist mal schnell eine IP-Adresse zu entbannen, habe ich mir selbst eine einfache Oberfläche gebaut, über die ich per Web den Status der einzelnen Jails abrufen kann einzelne IP-Adressen entbannen und manuell IP-Adressen bannen kann.

      Das System ist als docker container umgesetzt worden. Zur Kommunikation mit fail2ban wird die bereitgestellte fail2ban.sock Schnittstelle verwendet, die man dem Container bekannt machen muss.

      Wer interesse zum testen/ausprobieren hat:
      https://github.com/oweitman/fail2bancontrol

      325e3e86-68dd-4044-91f1-bdf93874648a-image.png
      7a46b6ff-6b2c-41a3-be00-2be991224f52-image.png

      posted in Off Topic
      OliverIO
      OliverIO
    • Lösung für den Fehler EISGIT

      Wer seinen adapter direkt im Verzeichnis /opt/iobroker/node_modules entwickeln möchte
      und dort ein git Repository initialisiert, könnte den Fehler EISGIT beim aktualisieren/installieren eines anderen Adapters erhalten.
      Dafür funktioniert folgende Lösung:

      1. Seinen eine Ebene direkt unter /opt/iobroker kopieren.
        Ein vorheriger Versuch den Adapter unter /home/pi zu kopieren scheiterte leider an der Auflösung von dependencys

      Die Option -r steht für rekursiv
      Die Option -p steht für alle Rechte und Infos über Eigentümer mitkopieren.

      cd /opt/iobroker/node_modules/
      sudo cp -rp <VerzeichnisnameAdapter>/ /opt/iobroker/
      
      1. Zur Sicherheit macht ihr dann noch eine Sicherheitskopie und löscht dann das Verzeichnis
      cd /opt/iobroker/node_modules/
      tar -cvpf /home/pi/<adaptername>.tar <VerzeichnisnameAdapter>/
      rm -r /opt/iobroker/node_modules/<VerzeichnisnameAdapter>
      
      1. Dann geht ihr in das Verzeichnis mit der Kopie und legt einen npm link an.
        Dadurch wird ein symlink ins globale Modulverzeichnis angelegt
      cd /opt/iobroker/<VerzeichnisnameAdapter>
      sudo npm link
      
      1. Zum Schluss dann in das Programmverzeichnis von iobroker
        Dort führt ihr wieder npm link plus den Paketnamen des Adapters aus (also das was in package.json drin steht.
        Das legt letztendlich einen symlink in das node_modules Verzeichnis an, so dass euer adapter an gewohnter Stelle verfügbar ist
      cd /opt/iobroker/
      npm link <AdapterPackagename>
      

      Danach habt ihr unter node_modules einen symlink zum anderen Verzeichnis.
      Ihr könnt dann ganz normal wieder im Adapterverzeichnis unter node_modulse arbeiten und npm
      stört sich nicht mehr an dem git-Repository.

      posted in Entwicklung
      OliverIO
      OliverIO
    • Test Adapter mytime

      Mein neuer Adapter soll sich um das Thema Zeit kümmern.
      Als erster Funktionsbaustein habe ich einen Countdown-Timer
      inklusive 2 Widgets umgesetzt.
      Eine detaillierte Beschreibung der Möglichkeiten ist in der Readme auf github zu finden

      Installation und Einrichtung

      Schritt 1 - Installation
      Der Adapter ist aktuell nur auf github verfügbar.
      Name des Repository ist https://github.com/oweitman/iobroker.mytime

      Schritt 2 - Instanz hinzufügen
      Der Adapter müsste dann im Abschnitt adapter im iobroker angezeigt werden.
      Manchmal kommt es vor, das insbesondere bei einem neuen Release mit Webänderungen (Widgets/Konfigurationsdialog) die Änderungen nicht sichtbar sind, muss evtl. auf der Kommandozeile folgender Befehl ausgeführt werden:
      iobroker upload mytime
      Im rechten Bereich in der Zeile des Adapters kann über den Plus-Knopf eine Instanz hinzugefügt werden

      Schritt 3 - Konfiguration

      Die Konfiguration ist relativ simpel. Es gibt nur wenige Felder.
      In den Eingabefeldern muss dem neuen Countdowntimer ein Name gegeben werden, sowie zur Erstkonfiguration die Angabe über die Dauer. diese kann aber später jederzeit über bestimmte Befehle auch von vis aus geändert werden.

      Über den Plus Knopf kann der Eintrag dann hinzugefügt werden. Das ändern und löschen eines Eintrags ist dann über die angezeigten Knöpfe hinter dem jeweiligen Countdown möglich.

      Schritt 4 - vis und widgets
      Aktuell gibt es 2 widgets

      • Countdown Plain (reine Textanzeige, formatierbar über einen Templatestring)
      • Countdown Circle (Ein Ring oder Kreis, der gemäß des Countdowns entsprechend abläuft.

      Eine detaillierte Beschreibung über die verfügbaren Datenpunkte, den verwertbaren States, die Verwendung der widgets inklusive einer Beispiel widgetgruppe für eine komplette Steuerung ist auf englisch in der Readme zu finden.

      Bei Fragen wie immer hier im Forum schreiben.

      Ich freue mich über reges testen und Vorschlag von Erweiterungen.

      Fehler können hier, aber auch auf github https://github.com/oweitman/ioBroker.mytime
      gemeldet werden.

      posted in Tester
      OliverIO
      OliverIO
    • RE: Adapter debuggen mit Chrome

      ich würde das hier gerne noch ergänzen für remote debugging, das heißt iobroker läuft nicht auf dem gleichen Rechner wie chrome, dann lautet der befehl in Anlehnung an dem obigen beispiel:

      node --inspect-brk=<ip-adresse iobroker>:9229 node_modules/iobroker.sayit/main.js --force --logs

      der parameter --inspect-brk sorgt im vergleich zu oben,
      das gleich zum start des debuggers auf der ersten Zeile deines Adapters ein breakpoint gesetzt wird
      Wer nicht immer den link zum start des debugs einzeln kopieren will, kann auch im chrome
      die folgende Seite aufrufen:
      chrome://inspect
      dann einmalig über configure die ip und port eures remotrechners genau wie beim inspect befehl eingeben.
      dort wird dann die debug session nach start des befehls angezeigt und kann mit einem klick gestartet werden.
      die chrome debug möglichkeiten finde ich fantastisch. ihr habt alle möglichkeiten, die ihr auch aus dem web-debugging kennt (breakpoints, auch mit Bedingungen, watch, callstack, scope inspection, consolenausgabe,etc.)
      bilder und englische Beschreibung befindet sich hier
      https://software.intel.com/en-us/xdk/articles/using-chrome-devtools-to-debug-your-remote-iot-nodejs-application

      falls noch nicht installiert ist auf dem iobroker rechner noch der node-inspector notwendig

      posted in Entwicklung
      OliverIO
      OliverIO
    • Neuer Adapter pi-hole2 für pihole>=V6
      Aktuelle Test Version 0.0.1
      Veröffentlichungsdatum Juni 2025
      Github Link https://github.com/oweitman/ioBroker.pi-hole2

      Nach dem der alte Adapter ab pihole v6 nicht mehr funktioniert, habe ich mich da mal dran gemacht. vgl auch forum.iobroker.net/topic/79939/pihole-adapter-ohne-funktion-nach-pi-hole-update-auf-v6

      Hier Adapter Beschreibung, Changelog etc.

      Funktionen

      Blockierung aktivieren oder deaktivieren

      Um die Werbung zu blockieren oder die Blockierung zu deaktivieren, kannst du einfach den Schalter im Datenpunkt Blocking verwenden.
      Der Datenpunkt BlockingTime ist dafür gedacht, die Blockierung vorübergehend zu deaktivieren – nach Ablauf der Zeit wird sie automatisch wieder eingeschaltet.
      Wenn du die Blockierung manuell aktivierst, passiert das sofort.

      Detaillierte Informationen – Übersicht

      Einige Informationen aus der Übersicht („Summary“) werden als eigene Datenpunkte unter Data.Summary angezeigt.
      Du kannst in der Adapterkonfiguration festlegen, ob diese Funktion aktiviert oder deaktiviert ist.
      Wenn sie aktiv ist, werden die entsprechenden Datenpunkte grün markiert – andernfalls rot.

      Detaillierte Informationen – Version

      Auch einige Daten aus dem Bereich „Version“ werden als einzelne Datenpunkte unter Data.Version bereitgestellt.
      Ob diese angezeigt werden, kannst du ebenfalls in der Konfiguration einstellen.
      Aktivierte Datenpunkte sind grün hervorgehoben, deaktivierte rot.

      Allgemeine sendTo-Funktion

      Mit der sendTo-Funktion kannst du Befehle direkt an dein Pi-hole-Gerät senden.
      Wenn du die API lokal ausprobieren möchtest, rufe einfach die Seite http://pi.hole/api/docs/# auf, gib dein Passwort ein und klicke auf den „Login“-Button.

      Weiteres

      Wer weitere Vorschläge zur Integration wichtiger Informationen machen möchte, kann gerne in seiner piholev6 installation die folgende Seite aufrufen: http://pi.hole/api/docs/, trägt dann oben sein Passwort ein und kann dann alle API-Endpunkte durchprobieren.

      gerne kann der adapter nun aus dem beta channel installiert werden.

      Fehler, Ideen, Anregungen dann bitte hier.

      Test nach Release im Beta bearbeitet

      posted in Tester
      OliverIO
      OliverIO
    • Dashboard für Temp/Hum mit Flot im grafana-Stil

      Hallo,

      ich habe mir ein neues Dashboard für meine Temperatur und Feuchtigkeitswerte gebaut.
      Da mir der Stil von Grafana sehr gut gefallen hat, aber zum einen Grafana für mein kleines Anzeigetablet zu viel Overhead mitschleift, habe ich das mittels Flot nachgebaut.
      Übrigens nutzt grafana unter der Oberfläche ebenfalls flot, allerdings unter mehreren logischen Schichten an modulen verborgen (flot->businesscharts->react ->angular).

      Mein dashboard sieht wie folgt aus:
      ff994e87-6862-4fa1-ae3d-8306a09df9c2-image.png
      Durch touch auf die jeweilige Anzeige kommt die Detailanzeige, die durch einen erneuten Touch auch wieder geschlossen wird. Aktuell hier immer nur die 24h Sicht. Erweitern will ich das noch mit 2 oder 3 anderen durch Knopf umschaltbare Zeiten.
      9d4f1403-3839-43b9-ba4d-005328fa8822-image.png

      Um felxibel in der Anpassung zu sein hab ich mir hier keine eigenen Widgets gebaut, sondern
      die Umsetzung mittels eines Adapters und verschiedenen basic HTML-widgets gelöst, die HTML und javascript-Befehle enthalten.
      Im Adapter sind die ganzen Bibliotheken für flot, flot-gauge und meinen ganzen Backendcode mit den Floteinstellungen enthalten.
      In den Basic-Html widgets zum einen eines, in dem die ganzen Bibliotheken ins vis geladen werden und dann für jede einzelne Anzeige ein kurzer javascript-Block mit den Parametern, die dort angezeigt werden sollen.

      Inhalt html widget zum libs-laden

      <script type="text/javascript" src="http://192.168.1.61:8082/mydashboard/js/flot.gauge/js/jquery.flot.js"></script>
      <script type="text/javascript" src="http://192.168.1.61:8082/mydashboard/js/flot/jquery.flot.time.js"></script>
      <script type="text/javascript" src="http://192.168.1.61:8082/mydashboard/js/flot.gauge/jquery.flot.gauge.grafana.js"></script>
      <script type="text/javascript" src="http://192.168.1.61:8082/mydashboard/js/index.js"></script>
      
      </div>
      <script>
      getGauge('Küche','humidity','#gaugekitchen',200,220);
      getTemp('Küche','temperature','#tempkitchen',200,220);
      </script>
      
      


      Beispiel html-widget zur Anzeige der Feuchtigkeitsanzeige

      <div id="gaugekitchen" style=""></div>
      


      Beispiel html-widget zur Anzeige der Temperaturanzeige

      <div id="tempkitchen" class="tempValue"></div>
      

      Mein Backendjavascript im Adapter sieht so aus:

      function getSQLDate(device,parameter,callback,type) {
        
          var range12h = 1000*60*60*12;
          var range24h = 1000*60*60*24;
          if (type=='24h') var myQuery = "SELECT ts, parameter, val FROM iobroker.mihome_th where device = '" + device +"' and parameter in ('humidity','temperature') AND val is not null AND ts > unix_timestamp()*1000-((60*60*24)*1000) ORDER BY parameter,ts";
          if (type=='last') var myQuery = "SELECT ts,val FROM iobroker.mihome_th where device = '" + device +"' and parameter = '" + parameter +"' and val is not NULL order by ts desc limit 1";
          
          vis.conn._socket.emit('sendTo', 'sql.0', 'query', myQuery, function (callback,type,result) {
      
              if (result.error) {
                  console.error(result.error);
              } else {
                  
                  if (type=='last') {
                      var datapoints = Object.keys(result.result).map(function(key) {
                          return [result.result[key].ts, result.result[key].val];
                      });
                  }
                  if (type=='24h') {
                      var datapoints = result.result.reduce(function(acc,obj) {
                          var key = obj['parameter'];
                              if(!acc[key]) {
                                  acc[key] = [];
                              }
                              acc[key].push([obj.ts,obj.val]);
                              return acc;
                      },[]);
                  }
                  callback(datapoints);
                  
              }
          }.bind(this,callback,type));
      
      }
      function humFormatter(v, axis) {
          return v.toFixed(axis.tickDecimals) + " %H";
      }
      function tempFormatter(v, axis) {
          return v.toFixed(axis.tickDecimals) + " °C";
      }    
      
      
                          
                         function getTemp(device,parameter,placeholder,width,height) {
                              var data = {};
                              data.device = device;
                              data.parameter = parameter;
                              data.placeholder = placeholder;
                              getSQLDate(device,parameter,doTemp.bind(data),'last');                                                
                          }
                           function doTemp(sqldata) {
                              var data = this;
                              $(data.placeholder).html(tempFormatter(sqldata[0][1]));
                              getTempUpdate(data);
                          }
                          function getTempUpdate(data) {
                              getSQLDate(data.device,data.parameter,doTempUpdate.bind(data),'last');                        
                          }
                          function doTempUpdate(sqldata) {
                              var data = this;
                              $(data.placeholder).html(tempFormatter(sqldata[0][1]));
                              setTimeout(function() {
                                  getTempUpdate(data);
                              }.bind(data),5000);
                          }
                          
                          function tempFormatter(value) {
                              return Math.round(value) + "&deg;C";
                          }
                          
                          function getTempchart(device,parameter,placeholder,width,height,type='inside') {
                              var data = {};
                              data.placeholder = placeholder;
                              data.width = width;                        
                              data.height = height;
                              data.device = device;
                              data.type = type;
                              getSQLDate(device,parameter,doTempchart.bind(data),'24h');  
                          }
                          function doTempchart(sqldata) {
                              var data = this;
                              var new_tempdata = $.extend(true,{},temp_data);
                              new_tempdata[0].data = sqldata.temperature;
                              new_tempdata[1].data = sqldata.humidity;
                              debugger;
                              $(data.placeholder).width(data.width)
                                  .height(data.height);
                              if (data.type=='inside') data.options = $.extend(true,{},temp_inside_options);
                              if (data.type=='outside') data.options = $.extend(true,{},temp_outside_options);
                              data.plot = $.plot(data.placeholder,[new_tempdata[0],new_tempdata[1]], data.options);
      
                          }
      
                          
                          function getGauge(device,parameter,placeholder,width,height,type='inside') {
                              var data = {};
                              data.placeholder = placeholder;
                              data.width = width;                        
                              data.height = height;
                              data.device = device;
                              data.parameter = parameter;
                              data.type = type;
                              getSQLDate(device,parameter,doGauge.bind(data),'last');                        
                          }
                          function doGauge(sqldata) {
                              var data = this;
                              var new_humdata = hum_data.slice(0);
                              new_humdata[0].data = sqldata;
                              new_humdata[0].label = this.device;
                              
                              if (data.type=='inside') data.options = $.extend(true,{},hum_threshold_inside,hum_options);
                              if (data.type=='outside') data.options = $.extend(true,{},hum_threshold_outside,hum_options);
                              $(this.placeholder).width(this.width)
                                  .height(this.height);
                              data.plot = $.plot(this.placeholder,new_humdata, data.options);
                                  $(this.placeholder).click(function() {
                                      getTempchart(data.device,null,'#tempdialog',880,500,data.type);
                                      $('#tempdialog').dialog({
                                          dialogClass: 'noTitle',
                                          minHeight: 527,
                                          maxHeight: 527,
                                          minWidth: 900,
                                          maxWidth: 900,
                                          position: [0,28],
                                      }).on('click',function(event){
                                          $('#tempdialog').dialog('close');
                                      });
                                      return false;
                                  });
                              getGaugeUpdate(data);
                          }
                          function getGaugeUpdate(data) {
                              getSQLDate(data.device,data.parameter,doGaugeUpdate.bind(data),'last');                        
                          }
                          function doGaugeUpdate(sqldata) {
                              
                              var data = this;
                              var new_humdata = hum_data.slice(0);
                              new_humdata[0].data = sqldata;
                              new_humdata[0].label = this.device;
                              data.options.series.gauges.value.color = getColor(data.options.series.gauges,new_humdata[0].data[0][1]);
                              data.plot = $.plot(this.placeholder,new_humdata, data.options);
                              setTimeout(function() {
                                  getGaugeUpdate(data);
                              }.bind(data),5000);
                          }
                          function getColor(gaugeOptionsi, data) {
                              var color;
                              for (var i = 0; i < gaugeOptionsi.threshold.values.length; i++) {
                                  var threshold = gaugeOptionsi.threshold.values[i];
                                  color = threshold.color;
                                  if (data < threshold.value) {
                                      break;
                                  }
                              }
                              return color;
                          }
         
        
      
      var hum_data = [
                          {
                          label: "device",
                          data: [[0, 50]]
                          }
                        ];
                                
                        
      var hum_options = {
          series: {
            gauges: {
              frame: false,
              gauge: {
                  min: 30,
                  max: 70,
                  width: 15,
                  background: {
                      color: "#2f2f32"
                  },
                  border: {
                      color: "#2f2f32",
                      width: 0
                  },
                  shadow: {
                      show: false,
                  }
              },
              value: {
                  background: {
                      color: null
                  },
                  font: {
                      size: 40, // a specified number, or 'auto'
                      family: ",sans-serif"
                  },
      //            color: "#73bf69"
              },
              cell: {
                  background: {
                      color: null
                  },
                  border: {
                      show: false,
                  },
                  margin: 5,
                  vAlign: "middle" // 'top' or 'middle' or 'bottom'
              },
              debug: {
                  log: true,
                  layout: true
              },
              show: true,
              label: {
                  show: true,
                  color: "#d8d9da",
                  margin: 0,
                  font: {
                      size: 20, // a specified number, or 'auto'
                      family: ",sans-serif"
                  }
              }
            }
          }
        };                    
      var hum_threshold_outside = {
          series: {
            gauges: {
              threshold: {
                  values: [
                      {
                          value: 40,
                          color: "#73bf69"
                      }, {
                          value: 60,
                          color: "#73bf69"
                      }, {
                          value: 100,
                          color: "#73bf69"
                      },
                  ]
              }
            }
          }
      };
      var hum_threshold_inside = {
          series: {
            gauges: {
              threshold: {
                  values: [
                      {
                          value: 40,
                          color: "#ee485a"
                      }, {
                          value: 60,
                          color: "#73bf69"
                      }, {
                          value: 100,
                          color: "#ee485a"
                      },
                  ]
              }
            }
          }
      };
      
      var temp_data =  [
                      { 
                          data: null, 
                          color: "#F2495C",
                          bars: {
                              fillColor: "#F2495C"
                          },                    
                          label: "temp"
                      },
                      {   data: null,
                          color: "#5794F2",
                          bars: {
                              fillColor: "#5794F2"
                          },                    
                          label: "hum", 
                          yaxis: 2 
                      }
                  ];
      var temp_inside_options =  {
                          series: {
                              lines: {
                                  lineWidth: 2,
                                  fill: 0.1
                              }
                          },
                          xaxes:  [ 
                                      { 
                                          mode: "time", 
                                          timeformat: "%H:%M",
                                          color: "#464648",
                                          font: {
                                              color: "#d8d9da"
                                          }
                                      } 
                          ],
                          yaxes:  [ 
                                      { 
                                          min: 5,
                                          max: 35,
                                          tickFormatter: tempFormatter,
                                          color: "#464648",
                                          font: {
                                              color: "#d8d9da"
                                          }
      
                                      }, 
                                      {
                                          min: 30,
                                          max: 90,
                                          alignTicksWithAxis: 1,
                                          position: "rigth",
                                          tickFormatter: humFormatter,
                                          color: "#464648",
                                          font: {
                                              color: "#d8d9da"
                                          }
      
                                      } 
                          ],
                          legend: { position: "sw" },
                          grid: {
                              borderWidth:    0,
                              color:          "#464648",
                              labelMarginX:   0,
      
                          }
                          };
      var temp_outside_options =  {
                          series: {
                              lines: {
                                  lineWidth: 2,
                                  fill: 0.1
                              }
                          },
                          xaxes:  [ 
                                      { 
                                          mode: "time", 
                                          timeformat: "%H:%M",
                                          color: "#464648",
                                          font: {
                                              color: "#d8d9da"
                                          }
                                      } 
                          ],
                          yaxes:  [ 
                                      { 
                                          min: -15,
                                          max: 35,
                                          tickFormatter: tempFormatter,
                                          color: "#464648",
                                          font: {
                                              color: "#d8d9da"
                                          }
      
                                      }, 
                                      {
                                          min: 30,
                                          max: 100,
                                          alignTicksWithAxis: 1,
                                          position: "rigth",
                                          tickFormatter: humFormatter,
                                          color: "#464648",
                                          font: {
                                              color: "#d8d9da"
                                          }
      
                                      } 
                          ],
                          legend: { position: "sw" },
                          grid: {
                              borderWidth:    0,
                              color:          "#464648",
                              labelMarginX:   0,
      
                          }
                          };
      
      

      Für die Historydaten habe ich mir auf Basis der iobroker-Daten noch eine eigene View gebaut, da ich mit den kryptischen Datenpunktbezeichnungen nicht arbeiten wollte, die mit der Xiaomi mihome-Adapter da produziert.

      Auch bei der Erstellung des dashboards in vis sind noch ein paar Dinge zu beachten, da die iobroker-App zusätzlich geladene Bibiotheken direkt nicht mag. Daher muss man die erstellte view nochmals über eine weitere view in einem iframe anzeigen. dann klappt es.

      Meine Lösung ist etwas frickelig und der Code auch nicht optimal strukturiert. Daher bei Übernahme des Konzeptes sollte man sich etwas besser mit javascript, html, sql und vis auskennen. Es ist keine CopyPaste-Lösung.

      Bei Interesse kann ich die anderen Bibliotheken ebenfalls bereitstellen (github).
      Bei noch mehr Interesse könnte ich das auch mal per teamviewer vorstellen oder gar per WebEx mehreren gleichzeitig.

      posted in Visualisierung
      OliverIO
      OliverIO
    • Wetter- / Stadt-Visualisierung mit KI

      Ich habe ein Prompt entdeckt, das ganz Nette Bilder für Wetter oder auch einfach nur ein Orts-Icons erstellen kann.
      Meine 3 Versuche mit dem jeweiligen Prompt poste ich hier einfach mal. Alle Bildgenerierungen sind der erste Versuch. Alles wurde in ChatGPT gemacht.

      A physically based reality (PBR) rendering of a miniature 3D city model placed in the center of a clean, soft background [pastel background color recommended]. The model features a compact grid with a detailed [Frankfurt Opera] and [Sachsenhausen] surrounded by green trees and streets. Above the model, the [Frankfurt am Main], [sunny], and [26] Celsius are displayed in clear white text, along with a [weather icon]. Realistic lighting with soft shadows highlights the model's architecture and materials, emphasizing clarity, simplicity, and aesthetic precision. Portrait ratio.

      ed3b9acb-7003-4d2b-9d9d-384930747d51-image.png

      A physically based reality (PBR) rendering of a miniature 3D city model placed in the center of a clean, soft background [light blue pastel]. The model features a compact grid with a detailed [Stuttgart Königsbau] and [Fernsehturm] surrounded by green trees and streets. Above the model, the [Stuttgart], [sonnig], and [26] Celsius are displayed in clear white text, along with a [weather icon]. Realistic lighting with soft shadows highlights the model's architecture and materials, emphasizing clarity, simplicity, and aesthetic precision. Portrait ratio.

      1258e8a6-4a5d-4b4f-bec7-e2e0d6911c2f-image.png

      A physically based reality (PBR) rendering of a miniature 3D city model placed in the center of a clean, soft background [light blue pastel]. The model features a compact grid with a detailed [Berlin Alexanderplatz] and [Berliner Fernsehturm] surrounded by green trees and streets. Above the model, the [Berlin], [sonnig], and [26] Celsius are displayed in clear white text, along with a [weather icon]. Realistic lighting with soft shadows highlights the model's architecture and materials, emphasizing clarity, simplicity, and aesthetic precision. Portrait ratio.

      7c364f89-2c54-49b0-ad84-2fcbf44d02b6-image.png

      posted in Visualisierung
      OliverIO
      OliverIO

    Latest posts made by OliverIO

    • RE: Const richtig bei timestamp (toTimeString)

      @matze55

      ja rechenaufgabe. im js standard ist sowas nicht enthalten

      function getRelativeDayLabel(dateInput) {
        const inputDate = new Date(dateInput); // Kann Date, Timestamp oder ISO-String sein
        const today = new Date();
        
        // Uhrzeit auf 00:00 setzen, damit nur der Tag verglichen wird
        today.setHours(0, 0, 0, 0);
        inputDate.setHours(0, 0, 0, 0);
      
        const diffInMs = today - inputDate;
        const diffInDays = diffInMs / (1000 * 60 * 60 * 24);
      
        if (diffInDays === 0) {
          return "heute";
        } else if (diffInDays === 1) {
          return "gestern";
        } else if (diffInDays > 1) {
          return "älter";
        } else {
          return "zukünftig"; // falls das Datum in der Zukunft liegt
        }
      }
      

      wo willst du das anwenden?

      posted in JavaScript
      OliverIO
      OliverIO
    • RE: Kann den Rpi Adapter auf meinem Raspberry nicht installieren

      @sonny

      ist opengpio 1.0.18 nicht etwas zu alt für ein aktuelles betriebssystem.
      aktuell ist 2.0.2
      https://www.npmjs.com/package/opengpio

      woher wird das referenziert?

      posted in ioBroker Allgemein
      OliverIO
      OliverIO
    • RE: [gelöst] Docker (VM) Container mit ioB (LXC) restart

      @australien

      ok dann ist es die id des environments
      also das was man am anfang, nach dem login im portainer auswählt.

      posted in Einsteigerfragen
      OliverIO
      OliverIO
    • RE: [gelöst] Docker (VM) Container mit ioB (LXC) restart

      @australien
      mach bitte bei endpoint id nochmal einen kommentar hin, das man das ausprobieren muss bzw evtl so nachgeschaut werden kann

      ae8ef6e4-67a1-4be1-a350-e378a90b90fa-image.png

      kannst du das bei dir verifizieren? da müsste dann bei dir eine 3 stehen

      posted in Einsteigerfragen
      OliverIO
      OliverIO
    • RE: Installation neues System

      @azrael42

      so wie geschrieben entweder proxmox, da hast du die wahl wischen VM,LXC oder ein in einer VM installiertem docker, was dir eine große Auswahl an images über dockerhub oder github container registry beschert.

      oder alternativ nur docker container direkt auf debian

      ich habe die 2.alternative
      das einzige was auf dem host betriebssystem installiert ist, ist docker.
      alles andere befindet sich in containern.
      ich habe auf dem host ein verzeichnis in dem sich alle volumes zu den container befinden, welche dann regelmäßig gesichert werden,
      sowie ein projekt verzeichnis indem sich hauptsächlich diverse dockerfiles befinden, bei denen ich den anbietern auf dockerhub nicht traue, falls sie morgen mir irgendwas unterschieben wollen
      da ich sogut wie nix an zusätzlicher hardware habe, ist das sicherlich (für mich) der beste weg.

      bei zusätzlicher hardware mag es sein, das eine VM besser ist, dann bist du mit proxmox flexibler.

      posted in Pflege des Betriebssystems
      OliverIO
      OliverIO
    • RE: Schreibrechte auf grafana.ini in Docker

      @legro

      genau

      posted in Grafana
      OliverIO
      OliverIO
    • RE: Schreibrechte auf grafana.ini in Docker

      @legro

      • in /etc/grafana liegen für grafana wichtige dateien ohne die das system nicht startet
      • wenn du nun ein leeres verzeichnis auf deinem LOKALEN dateisystem mappst, dann ist das verzeichnis IM container ebenfalls leer, ergo system startet nicht
      • der zwischenschritt mit /etc/grafana1 legt das zunächst leere verzeichnis unter diesem namen IN DEN container, du kopierst die dateien, änderst das mapping wieder auf /etc/grafana, so sind nun alle notwendigen dateien für grafana unter /etc/grafana verfügbar + sie liegen auch in deinem LOKALEN verzeichnis in dem du die bearbeiten kannst.

      ein docker image besteht aus verschiedenen schichten/layers, die sich der reihe nach ergänzen aber auch überlagern können, so das am ende für das betriebssystem aber nur eine einzige sicht darauf besteht.

      ich habe mal einen blogartikel gefunden, der das etwas visuell darstellt
      https://iammathew.com/blog/understanding-docker-images/filesystem

      das verzeichnis /etc/grafana mit seinen dateien ist nachdem du dein lokales verzeichnis da drauf gemountet hast immer noch da, da es aber von dem mount zu deinem lokalen leeren verzeichnis überlagert wird, halt für das betriebssystem leer und ohne die dateien mag grafana halt nicht starten.

      um notwendige dateien vollautomatisch auch in einem leeren gemounteten Verzeichnis nach aussen bereitzustellen, müsste der imageersteller etwas machen (bei container start prüfen ob alle notwendigen dateien da sind und die fehlenden kopieren) das machen aber viele nicht.

      wie schon gesagt, die grafana leute haben eher den environment weg eingeschlagen.

      posted in Grafana
      OliverIO
      OliverIO
    • RE: [gelöst] Docker (VM) Container mit ioB (LXC) restart

      @australien

      super,
      danke das du deinen code zum abschluss nochmal gepostet hast,
      dann können andere den wiederverwenden und ggfs erweitern.

      posted in Einsteigerfragen
      OliverIO
      OliverIO
    • RE: [gelöst] Docker (VM) Container mit ioB (LXC) restart

      @australien

      Portainer spiegelt dir die docker api nach draußen.
      Daher kannst du alle api Endpunkte die docker bereitstellt in dieser Art abfragen.

      Diese Information dürfte hier sein

      https://docs.docker.com/reference/api/engine/version/v1.51/#tag/Container/operation/ContainerInspect

      Das mit dem endpoint id ist interessant. Wusste ich auch nicht

      posted in Einsteigerfragen
      OliverIO
      OliverIO
    • RE: Schreibrechte auf grafana.ini in Docker

      @legro
      Na das mit den umgebungsvariablen hatte codierknecht und ich ziemlich am Anfang schon vorgeschlagen. Darauf bist du aber nicht eingegangen, weil ich davon ausgegangen bin das es dir nicht gefallen hat, bin ich dann den anderen Weg mit dir gegangen.

      Ich gehe davon aus, das man den Text immer vollständig liest und bei Dingen die man da nicht versteht nachfragt.

      posted in Grafana
      OliverIO
      OliverIO
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
    The ioBroker Community 2014-2023
    logo