NEWS
OpenEpaperLink - Script für Tastenabfrage
-
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.
-
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?
-
@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 ) {
-
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.
-
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); } }); }