Navigation

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

    NEWS

    • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?

    • Monatsrückblick – September 2025

    • Neues Video "KI im Smart Home" - ioBroker plus n8n

    • Profile
    • Following 1
    • Followers 11
    • Topics 64
    • Posts 7841
    • Best 1322
    • Groups 4

    OliverIO

    @OliverIO

    Meine Adapter und Widgets:
    TVProgram Github Forum
    Squeezebox Github Forum
    OpenLiga Github Forum
    RSSFeed Github Forum
    vis-2-widgets-rssfeed Github Forum
    MyTime Github Forum
    pi-hole 2 Github Forum
    vis-json-template Github Forum
    skiinfo Github Forum
    vis-mapwidgets Github Forum

    1646
    Reputation
    1058
    Profile views
    7841
    Posts
    11
    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: Nach Angriff auf node.js: Sofortmaßnahmen erforderlich?

      @bilberry

      da vor einer weile es schon einmal vorkam, habe ich mein damalig erstelltes skript mit den versionen der diesmal betroffenen bibliotheken erweitert.

      date mit endung ps1 ist für windows powershell
      datei mit endung sh ist für debian artige mit bash

      eine der dateien, passend zum betriebssystem in das hauptverzeichnis eurer iobroker installation kopieren, bei linux mit

      chmod +x compromised.sh
      

      noch für die ausführbarkeit sorgen und das
      programm starten.
      im idealfall erscheint dann folgende meldung

      🔍 Durchsuche Lockfiles in /home/iobroker/docker/volume/iobroker/dev/iobroker nach kompromittierten NPM-Paketen...
      ✅ Keine kompromittierten Pakete in Lockfiles gefunden.
      
      

      skripte werden ohne irgendeine garantie bereitgestellt

      compromised.sh
      compromised.ps1

      posted in ioBroker Allgemein
      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
    • RE: Nach Angriff auf node.js: Sofortmaßnahmen erforderlich?

      so nachdem die KI hart gearbeitet hat (hatte ich schon erwähnt das ich powershell hasse?)

      ist nun die paketliste in ein einheitliches format überführt worden.
      powershell und bash skripte laden nun diese datei und scannen dann ein im gleichen Verzeichnis befindliches lockfile.

      https://github.com/oweitman/compromisedPackages

      evtl auftretende fehler/probleme bitte hier oder im github melden.

      posted in ioBroker Allgemein
      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

    Latest posts made by OliverIO

    • RE: vis-canvas-gauges für VIS-2

      @wszene

      Ich bin gerade auf diesen thread gestoßen.
      Evtl ging es Anfang des Jahres noch nicht.
      Aber ich konnte das exakt so in vis2 v2.13.7 nachbauen

      123c6e19-b1d2-40ea-a383-9dd576ba3f66-image.png

      verwendeter adapter vis-canvas-gauges

      [
       {
         "tpl": "tplCGradialGauge",
         "data": {
           "g_fixed": false,
           "g_visibility": false,
           "g_css_font_text": false,
           "g_css_background": false,
           "g_css_shadow_padding": false,
           "g_css_border": false,
           "g_gestures": false,
           "g_signals": false,
           "g_last_change": false,
           "visibility-cond": "==",
           "visibility-val": 1,
           "visibility-groups-action": "hide",
           "factor": "1",
           "valueOffset": "0",
           "hCount": 2,
           "minorTicks": "4",
           "animation": "true",
           "needle": "true",
           "needleShadow": "true",
           "needleType": "select",
           "borders": "true",
           "borderOuterWidth": "2",
           "borderMiddleWidth": "2",
           "borderInnerWidth": "2",
           "borderShadowWidth": "2",
           "valueBox": true,
           "ticksAngle": "270",
           "startAngle": "45",
           "signals-cond-0": "==",
           "signals-val-0": true,
           "signals-icon-0": "/vis/signals/lowbattery.png",
           "signals-icon-size-0": 0,
           "signals-blink-0": false,
           "signals-horz-0": 0,
           "signals-vert-0": 0,
           "signals-hide-edit-0": false,
           "signals-cond-1": "==",
           "signals-val-1": true,
           "signals-icon-1": "/vis/signals/lowbattery.png",
           "signals-icon-size-1": 0,
           "signals-blink-1": false,
           "signals-horz-1": 0,
           "signals-vert-1": 0,
           "signals-hide-edit-1": false,
           "signals-cond-2": "==",
           "signals-val-2": true,
           "signals-icon-2": "/vis/signals/lowbattery.png",
           "signals-icon-size-2": 0,
           "signals-blink-2": false,
           "signals-horz-2": 0,
           "signals-vert-2": 0,
           "signals-hide-edit-2": false,
           "lc-type": "last-change",
           "lc-is-interval": true,
           "lc-is-moment": false,
           "lc-format": "",
           "lc-position-vert": "top",
           "lc-position-horz": "right",
           "lc-offset-vert": 0,
           "lc-offset-horz": 0,
           "lc-font-size": "12px",
           "lc-font-family": "",
           "lc-font-style": "",
           "lc-bkg-color": "",
           "lc-color": "",
           "lc-border-width": "0",
           "lc-border-style": "",
           "lc-border-color": "",
           "lc-border-radius": 10,
           "lc-zindex": 0,
           "oid": "0_userdata.0.value",
           "title": "Speed",
           "units": "km/H",
           "maxValue": "250",
           "highlightsFrom1": "200",
           "highlightsTo1": "225",
           "highlightsColor1": "#ff6666",
           "g_highlights-1": true,
           "highlightsFrom2": "225",
           "highlightsTo2": "250",
           "highlightsColor2": "#f50000",
           "g_highlights-2": true
         },
         "style": {
           "left": "283px",
           "top": "19px"
         },
         "widgetSet": "canvas-gauges",
         "_id": "i000001"
       }
      ]
      

      posted in Visualisierung
      OliverIO
      OliverIO
    • RE: Skript zur dynamischen Generierung Batterie/Akku Symbol

      Ich habe mal das Skript für das widget aus dem adapter jsontemplate umgesetzt

      https://forum.iobroker.net/topic/31521/test-widget-json-template/33?_=1762980256778

      Damit kann man dann ohne Datenpunkt, um das Bild zwischenzuspeichern, das Bild verwenden. Das Widget reagiert auch direkt auf veränderungen.

      posted in JavaScript
      OliverIO
      OliverIO
    • RE: Test Widget json template

      Ich habe mal das Battery-skript für jsontemplate umgesetzt, so das man ohne zusätzlichen Datenpunkt das Ergebnis direkt im Browser einbinden kann

      Das Template hier im widget einfach einfügen.
      Der Datenpunkt aus dem der Wert kommt ist als
      datapoint[1] einzutragen

      4398c0af-52fb-4920-a92f-6628eea0b476-image.png

      5ce0641d-f613-41a8-abb1-c8e30d0dd23f-image.png

      Die im folgenden thread beschriebenen Konfigurationsmöglichkeiten sind direkt zum Beginn des Template verfügbar:

      3276d915-f870-4974-b656-a2aa36f1abca-image.png
      https://forum.iobroker.net/topic/82868/skript-zur-dynamischen-generierung-batterie-akku-symbol?page=1

      <%
      //Ersteller: Ro75
      //Version: 1.0.6
      
      //configuration
      const decimalPlaces = 0,
      labelSuffix = '%',
      customLabel = null,
      showPercent = true,
      strongColors = false,
      colorScheme = 'default',
      showBolt = true,
      boltPos = 100,
      blinkBolt = false;
      %>
      
      <%
      const percent = dp[Object.keys(dp)[0]];
      
      // -------------------------------------------------------
      // Hilfsfunktionen
      // -------------------------------------------------------
      function clamp(v, a, b) {
         return Math.max(a, Math.min(b, v));
      }
      function uid(prefix = 'id') {
         return prefix + "-" + Math.random().toString(36).slice(2, 9);
      }
      function hslToRgb(h, s, l) {
         s /= 100;
         l /= 100;
         const k = n => (n + h / 30) % 12;
         const a = s * Math.min(l, 1 - l);
         const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
         return [Math.round(255 * f(0)), Math.round(255 * f(8)), Math.round(255 * f(4))];
      }
      
      function luminance(r, g, b) {
         const srgb = [r, g, b].map(c => {
             c /= 255;
             return (c <= 0.04045) ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
         });
         return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
      }
      const SAMPLE_POINTS = [{
             p: 0,
             w: 2
         }, {
             p: 5,
             w: 10
         }, {
             p: 10,
             w: 19
         }, {
             p: 15,
             w: 29
         }, {
             p: 20,
             w: 38
         }, {
             p: 25,
             w: 48
         }, {
             p: 30,
             w: 58
         }, {
             p: 35,
             w: 67
         }, {
             p: 40,
             w: 77
         }, {
             p: 45,
             w: 86
         }, {
             p: 50,
             w: 96
         }, {
             p: 55,
             w: 106
         }, {
             p: 60,
             w: 115
         }, {
             p: 65,
             w: 125
         }, {
             p: 70,
             w: 134
         }, {
             p: 75,
             w: 144
         }, {
             p: 80,
             w: 154
         }, {
             p: 85,
             w: 163
         }, {
             p: 90,
             w: 173
         }, {
             p: 95,
             w: 182
         }, {
             p: 100,
             w: 192
         }
      ];
      
      function interpolatedWidth(percent) {
         const p = clamp(percent, 0, 100);
         for (const s of SAMPLE_POINTS)
             if (s.p === p)
                 return s.w;
         let lower = SAMPLE_POINTS[0],
         upper = SAMPLE_POINTS[SAMPLE_POINTS.length - 1];
         for (let i = 0; i < SAMPLE_POINTS.length - 1; i++) {
             const a = SAMPLE_POINTS[i],
             b = SAMPLE_POINTS[i + 1];
             if (p > a.p && p < b.p) {
                 lower = a;
                 upper = b;
                 break;
             }
             if (p === b.p)
                 return b.w;
         }
         const t = (p - lower.p) / (upper.p - lower.p);
         return Math.round(lower.w + t * (upper.w - lower.w));
      }
      
      // -------------------------------------------------------
      // Farb-Schemata mit strongColors-Effekt
      // -------------------------------------------------------
      function getFillColor(p, strongColors, colorScheme) {
         let hue;
         let saturation;
         let lightness;
      
         switch ((colorScheme || 'default').toLowerCase()) {
         case 'green':
             hue = 120;
             saturation = strongColors ? 100 : 80;
             lightness = strongColors ? 25 + (p / 100) * 25 : 35 + (p / 100) * 30;
             break;
         case 'yellow':
             hue = 50;
             saturation = strongColors ? 100 : 85;
             lightness = strongColors ? 25 + (p / 100) * 30 : 35 + (p / 100) * 30;
             break;
         case 'blue':
             hue = 210;
             saturation = strongColors ? 100 : 75;
             lightness = strongColors ? 20 + (p / 100) * 25 : 35 + (p / 100) * 30;
             break;
         case 'red':
             hue = 0;
             saturation = strongColors ? 100 : 75;
             lightness = strongColors ? 20 + (p / 100) * 25 : 35 + (p / 100) * 30;
             break;
         case 'orange':
             hue = 30;
             saturation = strongColors ? 100 : 80;
             lightness = strongColors ? 20 + (p / 100) * 25 : 35 + (p / 100) * 30;
             break;
         case 'brown':
             hue = 25;
             saturation = strongColors ? 85 : 65;
             lightness = strongColors ? 20 + (p / 100) * 20 : 25 + (p / 100) * 25;
             break;
         case 'grey':
             hue = 0;
             saturation = strongColors ? 15 : 0;
             lightness = strongColors ? 20 + (p / 100) * 40 : 25 + (p / 100) * 40;
             break;
         case 'purple':
             hue = 275;
             saturation = strongColors ? 95 : 75;
             lightness = strongColors ? 25 + (p / 100) * 25 : 35 + (p / 100) * 30;
             break;
         case 'black':
             hue = 0;
             saturation = strongColors ? 10 : 0;
             lightness = strongColors ? 1 + (p / 100) * 27 : 3 + (p / 100) * 20;
             break;
         default: // Standard: grün → rot Verlauf
             hue = Math.round((p / 100) * 120);
             saturation = strongColors ? 100 : 90;
             lightness = strongColors ? 35 : 50;
             break;
         }
      
         return "hsl(" + hue + "," + saturation + "%," + lightness + "%)";
      }
      
      // -------------------------------------------------------
      // Hauptfunktion:
      // -------------------------------------------------------
      const raw = Number(percent);
      const p = clamp(Number.isFinite(raw) ? raw : 0, 0, 100);
      
      const viewBoxW = 264, viewBoxH = 129;
      const outer = {
         x: 20,
         y: 24,
         w: 200,
         h: 80,
         rx: 18
      };
      const inner = {
         x: 24,
         y: 28,
         h: 72,
         rx: 12
      };
      const maxInnerWidth = 192;
      
      const fillW = interpolatedWidth(p);
      const fillColor = getFillColor(p, strongColors, colorScheme);
      const nums = (fillColor.match(/-?\d+(\.\d+)?/g) || []).map(Number);
      const [hVal = 0, sVal = 0, lVal = 50] = nums;
      const [r, g, b] = hslToRgb(hVal, sVal, lVal);
      const lum = luminance(r, g, b);
      const textFill = lum > 0.55 ? '#000' : '#fff';
      const outlineColor = (textFill === '#fff') ? 'rgba(0,0,0,0.85)' : 'rgba(255,255,255,0.95)';
      
      const formattedValue = Number(p).toFixed(decimalPlaces);
      
      const formattedTrimmed = (decimalPlaces === 0) ? String(Math.round(Number(formattedValue))) : formattedValue;
      
      const displayText = customLabel ?? formattedTrimmed + labelSuffix;
      
      const fontSize = Math.max(12, Math.round(inner.h * 0.33 * 2.25));
      const textCenterX = inner.x + maxInnerWidth / 2;
      const textCenterY = inner.y + inner.h / 2;
      const TEXT_DY_EM = 0.35;
      
      const contact = {
         x: 224,
         y: 46,
         w: 20,
         h: 36
      };
      const contactCenterX = contact.x + contact.w / 2;
      const contactCenterY = contact.y + contact.h / 2;
      
      const boltViewBox = {
         w: 102.7,
         h: 186.8
      };
      const boltTargetH = outer.h * 1.7;
      const boltScale = boltTargetH / boltViewBox.h;
      const boltOffsetY = contactCenterY + 26;
      
      const clampedBoltPos = clamp(boltPos, 0, 100);
      const leftEdge = 0;
      const rightEdge = contactCenterX - 50;
      const baseX = leftEdge + ((rightEdge - leftEdge) * (clampedBoltPos / 100));
      let extraOffset = -30;
      if (clampedBoltPos === 0)
         extraOffset = -15;
      else if (clampedBoltPos === 100)
         extraOffset = 0;
      const boltX = baseX + extraOffset;
      const id = uid('b');
      %>
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 <%= viewBoxW %> <%= viewBoxH %>" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
      <% if (blinkBolt) { %>
         <style>
           @keyframes blinkBolt-<%= id %> {
             0%, 100% { 
               opacity: 1;
             }
             50% { 
               opacity: 0.6; 
             }
           }
           .blinking-bolt-<%= id %> {
             animation: blinkBolt-<%= id %> 1.8s ease-in-out infinite;
           }
         </style>
      <% } %>
         <defs>
         	<linearGradient id="glass-<%= id %>" x1="0" y1="0" x2="0" y2="1">
         		<stop offset="0%" stop-color="#ffffff" stop-opacity="0.80"/>
         		<stop offset="100%" stop-color="#ffffff" stop-opacity="0.10"/>
         	</linearGradient>
         	<linearGradient id="diagGlass-<%= id %>" x1="0" y1="0" x2="1" y2="1">
         		<stop offset="0%" stop-color="#ffffff" stop-opacity="0.75"/>
         		<stop offset="45%" stop-color="#ffffff" stop-opacity="0.22"/>
         		<stop offset="100%" stop-color="#ffffff" stop-opacity="0.03"/>
         	</linearGradient>
         	<pattern id="stripes-<%= id %>" width="8" height="8" patternUnits="userSpaceOnUse">
         		<rect width="8" height="8" fill="transparent"/>
         		<path d="M-1,6 l8,-6 M-1,10 l8,-6" stroke="#fff" stroke-opacity="0.08" stroke-width="1"/>
         	</pattern>
         	<clipPath id="clip-fill-<%= id %>">
         		<rect x="<%= inner.x %>" y="<%= inner.y %>" width="<%= fillW %>" height="<%= inner.h %>" rx="<%= inner.rx %>" ry="<%= inner.rx %>"/>
         	</clipPath>
         	<linearGradient id="boltGradient-<%= id %>" x1="8.7" x2="80.9" y1="17.1" y2="142.1" gradientUnits="userSpaceOnUse">
         		<stop offset="0" stop-color="#f7b23b"/>
         		<stop offset=".5" stop-color="#f7b23b"/>
         		<stop offset="1" stop-color="#f59e0b"/>
         	</linearGradient>
         	<symbol id="boltSymbol-<%= id %>" viewBox="0 0 102.7 186.8">
         		<path fill="url(#boltGradient-<%= id %>)" stroke="#000" stroke-width="6" stroke-linejoin="round" d="m34.8 2-32 96h32l-16 80 80-112h-48l32-64h-48z"/>
         	</symbol>
         </defs>
         <rect x="<%= outer.x %>" y="<%= outer.y %>" width="<%= outer.w %>" height="<%= outer.h %>" rx="<%= outer.rx %>" fill="#222" stroke="#ddd" stroke-width="4"/>
         <rect x="<%= inner.x %>" y="<%= inner.y %>" width="<%= fillW %>" height="<%= inner.h %>" rx="<%= inner.rx %>" ry="<%= inner.rx %>" fill="<%= fillColor %>"/>
         <g clip-path="url(#clip-fill-<%= id %>)">
         	<rect x="<%= inner.x %>" y="<%= inner.y %>" width="<%= fillW %>" height="<%= inner.h %>" rx="<%= inner.rx %>" fill="url(#stripes-<%= id %>)" opacity="0.95"/>
         	<rect x="<%= inner.x %>" y="<%= inner.y %>" width="<%= fillW %>" height="<%= inner.h %>" rx="<%= inner.rx %>" fill="url(#glass-<%= id %>)" opacity="0.25"/>
         </g>
         <rect x="<%= outer.x %>" y="<%= outer.y %>" width="<%= outer.w %>" height="<%= outer.h %>" rx="<%= outer.rx %>" fill="url(#diagGlass-<%= id %>)" opacity="0.9"/>
         <rect x="224" y="46" width="20" height="36" rx="6" fill="#ccc" stroke="#888" stroke-width="2"/>
         <% if (showBolt) { %>
         <use xlink:href="#boltSymbol-<%= id %>" class="blinking-bolt-<%= id %>" transform="translate(184, 90) scale(0.728051391862955) translate(-51.35, -93.4)"/>
         <% } %>
         <g transform="translate(<%= textCenterX %>, <%= textCenterY %>)">
         	<text text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="<%= fontSize %>" font-weight="700" fill="<%= textFill %>" stroke="<%= outlineColor %>" stroke-width="<%= Math.max(2, Math.round(fontSize * 0.15)) %>" paint-order="stroke" dy="<%= TEXT_DY_EM %>em">
               <%= displayText %>
         	</text>
         </g>
      </svg>
      

      Die Objektnotationen aus dem Skript mussten umformatiert werden, da diese durch vis ansonsten als bindings erkannt werden.

      posted in Tester
      OliverIO
      OliverIO
    • RE: Website mit Parser auslesen

      @dr-bakterius
      nein, wie oben geschrieben
      im parser adapter und per skript adapter mit fetch

      posted in ioBroker Allgemein
      OliverIO
      OliverIO
    • RE: Website mit Parser auslesen

      @dr-bakterius

      bei http error code 403 = forbidden, kommen erst gar keine daten
      da kommt nur der http header zurück

      posted in ioBroker Allgemein
      OliverIO
      OliverIO
    • RE: Quelle für Anzahl der Sonnenstunden?

      die dwd api scheint diese informationen enthalten

      6afc0bda-e881-49a7-b3e5-4bff00b0bd14-image.png

      https://dwd.api.bund.dev/
      https://dwd.api.proxy.bund.dev/v30/stationOverviewExtended?stationIds=10865,G005

      dieeinzelwerte sind nicht so gut dokumentiert.
      hier steht ein wenig dazu
      https://github.com/bundesAPI/dwd-api

      posted in ioBroker Allgemein
      OliverIO
      OliverIO
    • RE: Website mit Parser auslesen

      @homoran

      kein problem
      aber am quelltext liegt es nicht.
      der seitenbetreiber kann sehr gut zwischen browser und anderem abruf unterscheiden.
      als antwort kommt immer 403, was forbidden entspricht. selbst wenn ich den original fetch aus dem browser ausführe.
      vergleich des fetchs zwischen 2 funktionierende browser abfragen haben kein unterschied feststellen können.

      andere scheinen kein problem zu haben

      da mehr wie 10000 zeichen hier als datei
      europapark.txt

      posted in ioBroker Allgemein
      OliverIO
      OliverIO
    • RE: VIS Aktualisierung hängt mit 'Color picker Widgets' v2.0.3

      pull request ist hier

      https://github.com/ioBroker/ioBroker.vis-colorpicker/pull/79

      oweitman created this issue in ioBroker/ioBroker.vis-colorpicker

      open fix Phillips Hue widget #79

      posted in Visualisierung
      OliverIO
      OliverIO
    • RE: Website mit Parser auslesen

      @homoran sagte in Website mit Parser auslesen:

      @legerm wie sieht denn der Seitenquelltext aus?

      bei 403 kommt ja nix mit. da kommt nur der http header

      posted in ioBroker Allgemein
      OliverIO
      OliverIO
    • RE: Website mit Parser auslesen

      @legerm

      ist zwar sehr ähnlich aber dann kannst du copy/paste machen

      regex

      Crowd-Level:\s([\d\,]+)
      

      als typ musst du string nemen. number hat bei mir nicht geklappt, da wohl die number checkbox kaputt ist

      Nachtrag:
      sehe gerade das der Request forbidden ist.
      Da mag der Server wohl irgendwas am request nicht.
      habe das mal auch mit einem javascript fetch probiert.
      die geben sich wohl viel mühe das zu unterscheiden.

      dann bleibt nur noch es mit puppeteer zu probieren

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