Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Praktische Anwendungen (Showcase)
    4. OpenEpaperLink - Script für Tastenabfrage

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    OpenEpaperLink - Script für Tastenabfrage

    This topic has been deleted. Only users with topic management privileges can see it.
    • Eisbaeeer
      Eisbaeeer Developer last edited by Eisbaeeer

      Es kam schon öfter die Frage auf, wie man die Tasten an einem EPaper Display für OpenEpaperLink abfragen kann.
      Leider funktioniert der Open-Epaper-Link Adapter derzeit eher mäßig, deshalb habe ich die Funktion des TCP Sockets in das Script übernommen (Danke an @Beowolf ).

      Also was macht das Script?

      • Man erstellt sich mehrere Views, die auf dem Display angezeigt werden sollen.
      • Im Script gibt man die Parameter an
      • Mit den Tasten am Display kann man dann die Seiten "hoch und runterblättern".

      Ich verwende hier ein 4" Touch Display mit integriertem Accesspoint. Das geht aber natürlich auch mit jedem anderen Epaper Display mit Taster.
      Die Uhr wird jede Minute aktualisiert. Macht also nur auf einem Accesspoint Sinn. 😉
      Das Laden der nächsten Seite dauert etwa 5-6 Sekunden. Ganz unten gibt es auch ein Video.

      0d73277b-679b-43e4-b845-21727960e4f4-Clock.png

      2fa511c0-50d2-4cd7-b78f-98b8a9e6e490-Views.png


      {
      "settings": {
      "style": {
      "background_class": ""
      },
      "theme": "redmond",
      "sizex": "480",
      "sizey": "480",
      "gridSize": "",
      "snapType": null,
      "useAsDefault": false,
      "useBackground": false
      },
      "widgets": {
      "e00001": {
      "tpl": "tplScreenResolution",
      "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",
      "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
      },
      "style": {
      "left": "16px",
      "top": "512px"
      },
      "widgetSet": "basic"
      },
      "e00002": {
      "tpl": "tplSvgClock",
      "data": {
      "g_fixed": false,
      "g_visibility": false,
      "g_css_font_text": false,
      "g_css_background": true,
      "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",
      "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,
      "quadSize": "80",
      "textSize": "0",
      "isSeconds": false,
      "quadTickColor": "white",
      "quadColor": "white",
      "textColor": "white",
      "tickColor": "white",
      "handsColor": "white",
      "handsColorLine": "white"
      },
      "style": {
      "left": "0px",
      "top": "0px",
      "width": "480px",
      "height": "480px",
      "background": "black",
      "z-index": "0"
      },
      "widgetSet": "timeandweather"
      },
      "e00003": {
      "tpl": "tplTwSimpleDate",
      "data": {
      "g_fixed": false,
      "g_visibility": false,
      "g_css_font_text": true,
      "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",
      "prependZero": "true",
      "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,
      "shortYear": true,
      "shortWeekDay": false,
      "showWeekDay": false,
      "monthWord": false,
      "shortMonth": false,
      "noClass": true
      },
      "style": {
      "left": "19px",
      "top": "22px",
      "color": "white",
      "font-size": "xx-large",
      "z-index": "1"
      },
      "widgetSet": "timeandweather"
      }
      },
      "name": "Tag4Zoll1",
      "filterList": []
      }


      // #############################################
      // Author: Eisbaeeer
      // Date: 20250519
      //
      // Dieses Script ermöglicht es, mit einem OpenEpaperTag und Tastern an dem Display (oder touch mit 4" Display), mehrere View´s durchzublättern. Die Tasten werden zum vor- bzw. zurückblättern
      // verwendet. Bevor das Script verwendet werden kann, müssen die Views angelegt werden. Die Views werden durchnummeriert, beginnend bei 1. Der "Basis" Name der View muss unten konfiguriert werden.
      // Die Anzahl der verwendeten Seiten werden mit "pageCount" konfiguriert.
      // Für jede View muss noch der Selektor 'waitForSelectorArray' definiert werden. Im Beispiel wartet puppettier beim View1 auf den Selector '#w00042'.
      // Die Erste View ist eine Uhr, welche jede Minute aktualisiert wird.
      //
      // In dem Script wird die Socket-Verbindung von Beowolf verwendet (https://forum.iobroker.net/topic/66380/e-ink-display-openepaperlink-displayanzeige-mit-batterie/809?_=1747419968864)
      //
      // Version: 0.4 : 20250519 Added socket
      // Version: 0.3 : 20250518 AP-4" kann in Verbindung mit iobroker.open-epaper-llink Adapter mehrere Ansichten durchblättern
      // Version 0.2 : Bugfix async pictures
      // Version: 0.1 : Convert from Blockly to js
      //
      // Benötigt:
      // - Puppeteer Adapter
      // - Chrome Headless
      //
      // #############################################
      // Hier die Anpassungen vornehmen!

      const urlOfVISView = 'http://192.168.1.200:8082/vis/index.html?OpenEpaper#Tag4Zoll'; // Eure View oder URL vom Bild + Nummer der View beginnend bei 1
      const targetWidth = 480; // Breite des Displays
      const targetHeight = 480; // Höhe des Displays
      const cutoutX = 0; // Abstand Pixel von links für Screenshot
      const cutoutY = 0; // Abstand Pixel von oben für Screenshot
      const pquality = 100; // Bildqualität
      const pageCount = 3; // Anzahl der Seiten zum Durchblättern
      const waitForSelectorArray = ["","#w00042","#w00044","#w00053"]; // Letztes Element auf das Peppeteer wartet als Array
      const inputPath = "/tmp/Tag01_480x480"; // Screenshot temporär Format: /Pfad/prefix
      const accesspointIP = '192.168.1.78'; // Accesspoint IP Adresse
      const macAddress = '0000840B822F3728'; // MAC-Adresse des Displays anpassen
      const ditherValue = 1; // Setze den Dither-Wert (Farbanpassungen: 0 = View hat gleiche Farben | 1 = View hat andere Farben als das Display
      const autostart = true; // Websocket automatisch starten? true|false

      // ENDE Anpassungen! Ab hier nichts mehr ändern!
      // #############################################

      var buttons = '0_userdata.0.open-epaper-link.Tag_Buttons.' + macAddress + '.page';
      createState(buttons,1);
      var uploadUrl = 'http://' + accesspointIP + '/imgupload';
      var wakeupR = '0_userdata.0.open-epaper-link.Tag_Buttons.' + macAddress + '.wakeupReason';
      var wakeup = '0_userdata.0.open-epaper-link.Tag_Buttons.' + macAddress + '.lastseen';

      schedule("* * * * *", function() {
      console.log("Send clock page every minute if pageVal is 1");
      var pageVal = getState(buttons).val;
      console.log("Page: " + pageVal);
      let waitForSelector = waitForSelectorArray[pageVal];
      console.log("waitForSelector: " + waitForSelector);
      var view = urlOfVISView+pageVal;
      console.log("View: " + view);
      if ( pageVal == 1 ) {
      takeScreenshots(pageVal,waitForSelector,view);
      }
      });

      on(wakeup,function() {
      var pageVal = getState(buttons).val;
      if ( getState(wakeupR).val == 5 ) {
      if (pageVal >= pageCount) {
      setState(buttons, 1 );
      pageVal = 1;
      } else {
      pageVal++;
      setState(buttons,pageVal);
      }
      let waitForSelector = waitForSelectorArray[pageVal];
      console.log("waitForSelector: " + waitForSelector);
      var view = urlOfVISView+pageVal;
      console.log("View: " + view);
      takeScreenshots(pageVal,waitForSelector,view);
      }
      if ( getState(wakeupR).val == 4 ) {
      if (pageVal <= 1 ) {
      setState(buttons,pageCount);
      pageVal = pageCount;
      } else {
      pageVal--;
      setState(buttons,pageVal);
      }
      let waitForSelector = waitForSelectorArray[pageVal];
      console.log("waitForSelector: " + waitForSelector);
      var view = urlOfVISView+pageVal;
      console.log("View: " + view);
      takeScreenshots(pageVal,waitForSelector,view);
      }
      setState(wakeupR,0);
      });

      // Requirements
      const puppeteer = require('puppeteer');
      const axios = require('axios');
      const fs = require('fs');
      const FormData = require('form-data');

      async function takeScreenshots(pageVal,waitForSelector,view) {
      console.log("*** function takeScreenshot");
      sendTo('puppeteer.0', 'screenshot', {
      url: view,
      path: inputPath+macAddress+pageVal+'.jpg',
      width: targetWidth,
      height: targetHeight,
      quality: pquality,

          waitOption: {
              waitForSelector: waitForSelector,  
              waitForTimeout: 10000              
          },
       
          fullPage: false,                       
       
          clip: {         
              x: cutoutX,                        
              y: cutoutY,                        
              width: targetWidth,                
              height: targetHeight               
          }
      }, obj => {
        if (obj.error) {
          log(`Error taking screenshot: ${obj.error.message}`, 'error');
        } else {
          // the binary representation of the image is contained in `obj.result`
          log(`Successfully took screenshot: ${obj.result}`);
          console.log("*** upload Image");
          // uploading Screenshot
          async function uploadImage() {
      	try {
      		// Prüfen, ob die Datei existiert
      		if (!fs.existsSync(inputPath+pageVal+'.jpg')) {
      		throw new Error(`Datei nicht gefunden: ${inputPath+macAddress+pageVal+'.jpg'}`);
      	}
      // FormData erstellen
      const form = new FormData();
      form.append("mac", macAddress);
      form.append("dither", ditherValue); // Dither-Parameter hinzufügen
      form.append("file", fs.createReadStream(inputPath+macAddress+pageVal+'.jpg'));
      // POST-Anfrage senden
      const response = await axios.post(uploadUrl, form, {
      headers: {
      	...form.getHeaders(),
      },
      });
      
      console.log('Upload erfolgreich:', response.data);
      } catch (error) {
      if (error.response) {
      	console.error('Server-Antwort:', error.response.status, error.response.data);
      } else if (error.request) {
      	console.error('Keine Antwort erhalten:', error.request);
      } else {
      	console.error('Fehler beim Hochladen:', error.message);
      }
      }
      }
      uploadImage();
      }
      })
      

      }

      const WebSocket = require('ws');
      const http = require('http');
      const wsUrl = ws://${accesspointIP}/ws; // WebSocket-URL

      let ws;
      let pingInterval;
      let scriptStopping = false; // Flag, um zu prüfen, ob das Skript gestoppt wird

      const controlState = '0_userdata.0.open-epaper-link.Start'; // Datenpunkt zur Steuerung des Skripts - anhalten oder starten

      function ensureOpenEPaperLinkFolderExists(callback) {
      const OpenEPaperLinkFolderPath = '0_userdata.0.open-epaper-link.Tag_Buttons';
      getObject(OpenEPaperLinkFolderPath, (err, obj) => {
      if (err || !obj) {
      setObject(OpenEPaperLinkFolderPath, {
      type: 'channel',
      common: { name: 'Open E-Paper Link' },
      native: {}
      }, callback);
      } else {
      callback();
      }
      });
      }

      function ensureChannelExists(path, alias, callback) {
      getObject(path, (err, obj) => {
      if (err || !obj) {
      setObject(path, {
      type: 'channel',
      common: { name: alias || 'Unbekanntes Gerät' },
      native: {}
      }, callback);
      } else if (obj.common.name !== alias) {
      extendObject(path, { common: { name: alias } }, callback);
      } else {
      callback();
      }
      });
      }

      function createStateAndSet(statePath, value) {
      setObject(statePath, {
      type: 'state',
      common: {
      name: statePath.split('.').pop(),
      type: 'string',
      role: 'value',
      read: true,
      write: true
      },
      native: {}
      }, (err) => {
      if (!err) {
      setState(statePath, String(value), true);
      }
      });
      }

      function updateStateIfChanged(statePath, value) {
      getState(statePath, (err, state) => {
      if (err || !state) {
      createStateAndSet(statePath, String(value));
      } else if (state.val !== String(value)) {
      setState(statePath, String(value), true);
      }
      });
      }

      function fetchDimensions(hwType, callback) {
      const hwTypeHex = hwType.toString(16).padStart(2, '0').toUpperCase(); // Convert hwType to two-digit uppercase hexadecimal
      const url = http://${accesspointIP}/tagtypes/${hwTypeHex}.json;
      http.get(url, (res) => {
      let data = '';
      res.on('data', (chunk) => data += chunk);
      res.on('end', () => {
      if (res.statusCode === 200) {
      try {
      const dimensions = JSON.parse(data);
      callback(null, dimensions);
      } catch (e) {
      callback(Error parsing JSON from ${url}: ${e});
      }
      } else {
      callback(HTTP Error ${res.statusCode} from ${url});
      }
      });
      }).on('error', (err) => {
      callback(Error fetching ${url}: ${err.message});
      });
      }

      function handleHWType(basePath, hwType) {
      createStateAndSet(${basePath}.hwType, String(hwType)); // Save hwType as a state
      fetchDimensions(hwType, (err, dimensions) => {
      if (!err && dimensions) {
      createStateAndSet(${basePath}.height, String(dimensions.height));
      createStateAndSet(${basePath}.width, String(dimensions.width));
      createStateAndSet(${basePath}.name, String(dimensions.name));
      if (dimensions.colors) {
      createStateAndSet(${basePath}.colors, String(dimensions.colors));
      }
      if (dimensions.colortable) {
      createStateAndSet(${basePath}.colortable, JSON.stringify(dimensions.colortable));
      }
      } else {
      // console.error(Failed to fetch or set dimensions for hwType ${hwType}: ${err});
      }
      });
      }

      function connectWebSocket() {
      if (scriptStopping) {
      return; // Wenn das Skript gestoppt wird, keine Verbindung mehr herstellen
      }

      ws = new WebSocket(wsUrl);
      ws.on('open', function open() {
          // console.log('Verbunden mit WebSocket');
          startHeartbeat();
      });
      
      ws.on('message', function incoming(data) {
          // console.log('Daten empfangen:', data);
          if (data) {
              try {
                  let parsedData = JSON.parse(data);
                  // console.log('Verarbeitete Daten:', JSON.stringify(parsedData, null, 2));
                  handleData(parsedData);
              } catch (err) {
                  // console.error('Fehler bei der Verarbeitung der Daten:', err);
              }
          } else {
              // console.log('Keine Daten oder leere Nachricht empfangen');
          }
      });
      
      ws.on('close', function close() {
          if (!scriptStopping) {
              // console.log('WebSocket-Verbindung geschlossen, versuche neu zu verbinden...');
              clearInterval(pingInterval);
              setTimeout(connectWebSocket, 5000);
          }
      });
      
      ws.on('error', function error(err) {
          // console.error('WebSocket-Fehler:', err);
      });
      

      }

      function startHeartbeat() {
      pingInterval = setInterval(() => {
      if (ws.readyState === WebSocket.OPEN) {
      ws.ping(() => {
      // console.log('Ping sent');
      });
      }
      }, 10000); // Send ping every 10 seconds

      ws.on('pong', () => {
          // console.log('Pong received');
      });
      

      }

      function handleData(parsedData) {
      if (parsedData.tags && Array.isArray(parsedData.tags)) {
      parsedData.tags.forEach(tag => {
      let basePath = 0_userdata.0.open-epaper-link.Tag_Buttons.${tag.mac.replace(/:/g, '')};
      ensureChannelExists(basePath, tag.alias, () => {
      Object.keys(tag).forEach(key => {
      let statePath = ${basePath}.${key};
      let value = tag[key];
      updateStateIfChanged(statePath, value);
      if (key === 'hwType') {
      handleHWType(basePath, tag.hwType);
      }
      });
      });
      });
      }
      }

      function disconnectWebSocket() {
      if (ws) {
      ws.close();
      ws = null;
      }
      clearInterval(pingInterval);
      }

      // Skript-Start und -Stopp basierend auf einem Datenpunkt steuern
      function setupScriptControl() {
      if (autostart == true) {
      ensureOpenEPaperLinkFolderExists(connectWebSocket);
      }
      setObject(controlState, {
      type: 'state',
      common: {
      name: 'EPaper Script Control',
      type: 'boolean',
      role: 'switch',
      read: true,
      write: true,
      def: false
      },
      native: {}
      });

      on({id: controlState, change: 'ne'}, (obj) => {
          const state = obj.state.val;
          if (state) {
              // Skript starten
              scriptStopping = false;
              ensureOpenEPaperLinkFolderExists(connectWebSocket);
          } else {
              // Skript stoppen
              scriptStopping = true;
              disconnectWebSocket();
              // console.log('Skript beendet durch Steuer-Datenpunkt');
          }
      });
      

      }

      // Initiale Einrichtung
      setupScriptControl();

      6807aa39-be12-4cbf-85e1-666b3c46749b-20250519_202102.jpg
      64c057dd-e8d9-428f-80c0-c3d6e928ddc0-20250519_202121.jpg
      674ec975-4bd9-482d-81b0-7e96eabf10ce-20250519_202136.jpg

      https://youtube.com/shorts/G4g4ynx-t_k?feature=share

      1 Reply Last reply Reply Quote 1
      • B
        Beowolf last edited by

        @eisbaeeer

        Fein, fein. Danke dafür.

        Ich habe das Skript jetzt noch nicht probiert.

        Besteht mit dem Skript auch die Möglichkeit, das ich mit den Tasten auch eine z.B. Lampe über ioBroker schalten kann?

        Eisbaeeer 1 Reply Last reply Reply Quote 0
        • Eisbaeeer
          Eisbaeeer Developer @Beowolf last edited by Eisbaeeer

          @beowolf So direkt erstmal nicht. Dazu müsstest du das Script umbauen. Vorstellbar wäre, dass du nur eine Taste zum durchblättern benutzt und die andere zum Toggeln oder schalten.
          Im Code findest du das Abfragen der Tasten hier:

          on(wakeup,function() {
          var pageVal = getState(buttons).val;
          if ( getState(wakeupR).val == 5 ) {
          if (pageVal >= pageCount) {
          setState(buttons, 1 );
          pageVal = 1;
          } else {
          pageVal++;
          setState(buttons,pageVal);
          }
          let waitForSelector = waitForSelectorArray[pageVal];
          console.log("waitForSelector: " + waitForSelector);
          var view = urlOfVISView+pageVal;
          console.log("View: " + view);
          takeScreenshots(pageVal,waitForSelector,view);
          }
          if ( getState(wakeupR).val == 4 ) {
          if (pageVal <= 1 ) {
          setState(buttons,pageCount);
          pageVal = pageCount;
          } else {
          pageVal--;
          setState(buttons,pageVal);
          }
          let waitForSelector = waitForSelectorArray[pageVal];
          console.log("waitForSelector: " + waitForSelector);
          var view = urlOfVISView+pageVal;
          console.log("View: " + view);
          takeScreenshots(pageVal,waitForSelector,view);
          }
          setState(wakeupR,0);
          });
          

          "wakeupR" : 4 ist die linke Taste, 5 die rechte Taste.

          if ( getState(wakeupR).val == 4 ) {
          
          1 Reply Last reply Reply Quote 0
          • B
            Beowolf last edited by Beowolf

            Habe das Skript mal NUR für die Button geändert. Es wird kein Inhalt an die TAGs gesendet.

            //
            // #############################################
            //
            // Dieses Script ermöglicht die Buttonabfrage.
            //
            // In dem Script wird die Socket-Verbindung von Beowolf verwendet (https://forum.iobroker.net/topic/66380/e-ink-display-openepaperlink-displayanzeige-mit-batterie/809?_=1747419968864)
            //
            // Ein erweitertes Skript von Eisbaeeer ist hier (https://forum.iobroker.net/topic/81101/openepaperlink-script-für-tastenabfrage )
            //
            // Die Button-States werden unter dem konfigurierten basePathPrefix erzeugt.
            //
            // #############################################
            
            // #############################################
            //
            // Konfigurierbare Variablen - Hier die Anpassungen vornehmen!
            //
            
            const controlStatePath = '0_userdata.0.Buttons-DG-TAGs';   // Basis für controlState und basePath
            const controlState = `${controlStatePath}.Start`;          // Skript starten oder beenden
            const basePathPrefix = `${controlStatePath}.Tag_Buttons`;  // Basis-Pfad für Button-States
            const accesspointIP = '192.168.49.187';                    // Accesspoint IP Adresse
            
            //
            // ENDE Anpassungen! Ab hier nichts mehr ändern!
            // #############################################
            
            
            
            
            
            const wsUrl = `ws://${accesspointIP}/ws`;
            const WebSocket = require('ws');
            
            let ws;
            let pingInterval;
            let scriptStopping = false;
            
            function connectWebSocket() {
                if (scriptStopping) return;
                ws = new WebSocket(wsUrl);
            
                ws.on('open', () => {
                    console.log('WebSocket verbunden');
                    startHeartbeat();
                });
            
                ws.on('message', (data) => {
                    try {
                        const parsed = JSON.parse(data);
                        handleData(parsed);
                    } catch (err) {
                        // Parsing-Fehler ignorieren
                    }
                });
            
                ws.on('close', () => {
                    clearInterval(pingInterval);
                    if (!scriptStopping) setTimeout(connectWebSocket, 5000);
                });
            
                ws.on('error', () => {
                    // WebSocket-Fehler ignorieren
                });
            }
            
            function startHeartbeat() {
                pingInterval = setInterval(() => {
                    if (ws.readyState === WebSocket.OPEN) {
                        ws.ping();
                    }
                }, 10000);
            }
            
            function handleData(parsedData) {
                if (!parsedData.tags || !Array.isArray(parsedData.tags) || parsedData.tags.length === 0) {
                    return;
                }
            
                parsedData.tags.forEach(tag => {
                    if (!tag.mac) return;
            
                    const macClean = tag.mac.replace(/:/g, '');
                    const basePath = `${basePathPrefix}.${macClean}`;
            
                    ensureChannelExists(basePath, tag.alias || tag.name || 'Unbenannt', () => {
                        if ('wakeupReason' in tag) {
                            const statePath = `${basePath}.wakeupReason`;
                            updateStateIfChanged(statePath, tag.wakeupReason);
                        }
                    });
                });
            }
            
            function ensureChannelExists(id, name, callback) {
                getObject(id, (err, obj) => {
                    if (!obj) {
                        setObject(id, {
                            type: 'channel',
                            common: {
                                name: name
                            },
                            native: {}
                        }, callback);
                    } else {
                        callback();
                    }
                });
            }
            
            function updateStateIfChanged(id, value) {
                if (value === null || value === undefined) return;
            
                const isPrimitive = val => ['string', 'number', 'boolean'].includes(typeof val);
            
                let storedValue = value;
                let valueType = typeof value;
            
                if (!isPrimitive(value)) {
                    try {
                        storedValue = JSON.stringify(value);
                        valueType = 'string';
                    } catch {
                        return;
                    }
                }
            
                getState(id, (err, state) => {
                    if (err || !state) {
                        setObject(id, {
                            type: 'state',
                            common: {
                                name: id.split('.').pop(),
                                type: valueType,
                                role: 'value',
                                read: true,
                                write: false
                            },
                            native: {}
                        }, () => setState(id, storedValue, true));
                    } else if (state.val !== storedValue) {
                        setState(id, storedValue, true);
                    }
                });
            }
            
            function disconnectWebSocket() {
                if (ws) {
                    ws.close();
                    ws = null;
                }
                clearInterval(pingInterval);
            }
            
            function setupControl() {
                setObject(controlState, {
                    type: 'state',
                    common: {
                        name: 'EPaper Script Control',
                        type: 'boolean',
                        role: 'switch',
                        read: true,
                        write: true,
                        def: true
                    },
                    native: {}
                });
            
                on({ id: controlState, change: 'ne' }, (obj) => {
                    if (obj.state.val === true) {
                        scriptStopping = false;
                        connectWebSocket();
                    } else {
                        scriptStopping = true;
                        disconnectWebSocket();
                    }
                });
            
                getState(controlState, (err, state) => {
                    if (!err && state && state.val === true) {
                        connectWebSocket();
                    }
                });
            }
            
            setupControl();
            

            Hier wird jetzt nur der Datenpunkt "wakeupReason" angelegt.

            1 Reply Last reply Reply Quote 1
            • B
              Beowolf last edited by Beowolf

              Hier ein geändertes Skript für die, die mehrere APs haben.

              //
              // #############################################
              //
              // Dieses Script ermöglicht die Buttonabfrage.
              //
              // In dem Script wird die Socket-Verbindung von Beowolf verwendet (https://forum.iobroker.net/topic/66380/e-ink-display-openepaperlink-displayanzeige-mit-batterie/809?_=1747419968864)
              //
              // Ein erweitertes Skript von Eisbaeeer ist hier (https://forum.iobroker.net/topic/81101/openepaperlink-script-für-tastenabfrage )
              //
              // Die Button-States werden unter dem konfigurierten basePathPrefix erzeugt.
              //
              // #############################################
              
              // #############################################
              //
              // Konfigurierbare Variablen - Hier die Anpassungen vornehmen!
              //
              
              
              const rootPath = '0_userdata.0'; // Bei Bedarf anpassen
              const controlRoot = 'EPaperControl'; // Oberordner für Steuerung
              const buttonRoot = 'Buttons'; // Unterordner für Buttons
              
              const accesspoints = [
                  { location: 'Erdgeschoss', ip: '192.168.49.185' }, // Accesspoint IP Adresse
                  { location: 'Obergeschoss', ip: '192.168.49.186' }, // Accesspoint IP Adresse
                  { location: 'Dachgeschoss', ip: '192.168.49.187' }, // Accesspoint IP Adresse
                  { location: 'Hühnerhaus', ip: '192.168.49.139' } // Accesspoint IP Adresse
              ];
              
              //
              // ENDE Anpassungen! Ab hier nichts mehr ändern!
              // #############################################
              //
              
              // Automatisch generierte Pfade für jeden Accesspoint
              accesspoints.forEach(ap => {
                  ap.name = `${ap.location}-AP`;
                  ap.controlState = `${rootPath}.${controlRoot}.${ap.location}.Start`;
                  ap.buttonPathPrefix = `${rootPath}.${controlRoot}.${ap.location}.${buttonRoot}`;
              });
              
              const WebSocket = require('ws');
              
              let wsConnections = {};
              let pingIntervals = {};
              let scriptStatus = {};
              
              // Initialisierung der Steuerobjekte
              accesspoints.forEach(ap => {
                  scriptStatus[ap.name] = false;
              
                  setObject(ap.controlState, {
                      type: 'state',
                      common: {
                          name: `Start/Stop ${ap.name}`,
                          type: 'boolean',
                          role: 'switch',
                          read: true,
                          write: true,
                          def: false
                      },
                      native: {}
                  });
              
                  on({ id: ap.controlState, change: 'ne' }, (obj) => {
                      if (obj.state.val === true) {
                          scriptStatus[ap.name] = true;
                          connectWebSocket(ap.name, ap.ip);
                      } else {
                          scriptStatus[ap.name] = false;
                          disconnectWebSocket(ap.name);
                      }
                  });
              
                  getState(ap.controlState, (err, state) => {
                      if (!err && state && state.val === true) {
                          scriptStatus[ap.name] = true;
                          connectWebSocket(ap.name, ap.ip);
                      }
                  });
              });
              
              function connectWebSocket(name, ip) {
                  if (wsConnections[name]) return;
              
                  const url = `ws://${ip}/ws`;
                  const ws = new WebSocket(url);
                  wsConnections[name] = ws;
              
                  ws.on('open', () => {
                      console.log(`WebSocket ${name} verbunden (${ip})`);
                      pingIntervals[name] = setInterval(() => {
                          if (ws.readyState === WebSocket.OPEN) {
                              ws.ping();
                          }
                      }, 10000);
                  });
              
                  ws.on('message', (data) => {
                      try {
                          const parsed = JSON.parse(data);
                          handleData(name, parsed);
                      } catch {
                          // Parsing-Fehler ignorieren
                      }
                  });
              
                  ws.on('close', () => {
                      clearInterval(pingIntervals[name]);
                      delete wsConnections[name];
                      if (scriptStatus[name]) {
                          setTimeout(() => connectWebSocket(name, ip), 5000);
                      }
                  });
              
                  ws.on('error', () => {
                      // Fehler ignorieren
                  });
              }
              
              function disconnectWebSocket(name) {
                  if (wsConnections[name]) {
                      wsConnections[name].close();
                      clearInterval(pingIntervals[name]);
                      delete wsConnections[name];
                      delete pingIntervals[name];
                  }
              }
              
              function handleData(apName, parsedData) {
                  const ap = accesspoints.find(a => a.name === apName);
                  if (!ap || !scriptStatus[ap.name]) return;
              
                  if (!parsedData.tags || !Array.isArray(parsedData.tags) || parsedData.tags.length === 0) return;
              
                  parsedData.tags.forEach(tag => {
                      if (!tag.mac) return;
              
                      const macClean = tag.mac.replace(/:/g, '');
                      const basePath = `${ap.buttonPathPrefix}.${macClean}`;
              
                      ensureChannelExists(basePath, tag.alias || tag.name || 'Unbenannt', () => {
                          if ('wakeupReason' in tag) {
                              const statePath = `${basePath}.wakeupReason`;
                              updateStateIfChanged(statePath, tag.wakeupReason);
                          }
                      });
                  });
              }
              
              function ensureChannelExists(id, name, callback) {
                  getObject(id, (err, obj) => {
                      if (!obj) {
                          setObject(id, {
                              type: 'channel',
                              common: { name: name },
                              native: {}
                          }, callback);
                      } else {
                          callback();
                      }
                  });
              }
              
              function updateStateIfChanged(id, value) {
                  if (value === null || value === undefined) return;
              
                  const isPrimitive = val => ['string', 'number', 'boolean'].includes(typeof val);
              
                  let storedValue = value;
                  let valueType = typeof value;
              
                  if (!isPrimitive(value)) {
                      try {
                          storedValue = JSON.stringify(value);
                          valueType = 'string';
                      } catch {
                          return;
                      }
                  }
              
                  getState(id, (err, state) => {
                      if (err || !state) {
                          setObject(id, {
                              type: 'state',
                              common: {
                                  name: id.split('.').pop(),
                                  type: valueType,
                                  role: 'value',
                                  read: true,
                                  write: false
                              },
                              native: {}
                          }, () => setState(id, storedValue, true));
                      } else if (state.val !== storedValue) {
                          setState(id, storedValue, true);
                      }
                  });
              }
              
              1 Reply Last reply Reply Quote 0
              • First post
                Last post

              Support us

              ioBroker
              Community Adapters
              Donate

              966
              Online

              31.7k
              Users

              79.7k
              Topics

              1.3m
              Posts

              2
              5
              140
              Loading More Posts
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes
              Reply
              • Reply as topic
              Log in to reply
              Community
              Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
              The ioBroker Community 2014-2023
              logo