Ich denke ja, dass ich so etwas gelesen habe. Allerdings nutze ich Matter nicht. Ich habe keinen Bedarf dafür.
Beiträge von eiche
-
-
Ich fand dazu bisher nichts. Aktuell arbeite ich überwiegend mit meinen Gen 2 Shelly.
-
Ich versuche herauszufinden, wie die Angabe von Speichergrenze(n) zu verstehen ist.
Ich fand dazu u.a. folgende Seite auf github zur Speicheroptimierung: https://github.com/LeivoSepp/Shelly-Memory-Optimization (Die Tipps sind mitunter gut zu gebrauchen, allerdings ist Stringsuche statt parsen nicht wirklich ernst zu nehmen.)
Dort steht u.a. "Shelly devices have only 25KB of memory for scripts, shared between runtime and peak usage."
Diese Aussage ist nicht hinreichend eindeutig.
Stehen 25KB für alle aktiven Skripte auf einem Shelly zur Verfügung oder für jedes Skript 25KB? Dies ist wesentlich für eine evtl. Aufsplittung auf mehrere Skripte.
Der RPC "Shelly.GetStatus" liefert u.a. mem_used, mem_peak und mem_free. Dabei fällt auf, dass der mem_free ert bei allen Skripten auf einem Shelly gleich sind, auch bei nicht aktiven Skripten.
Vermutlich gelten die 25KB für die Vereinigung aller Skripte auf einem Shelly. Dies hätte zur Folge, dass eine Aufteilung auf mehrere Skripte keinen Memory Vorteil brächte.
-
als wenn der Verbraucher, der als Schalter an L und SW angeschlossen ist
Wie willst du das denn beschalten?
Einen Verbraucher originär als Schalter zu nutzen ist zumindest sehr nutzungsfremd, imho tatsächlich unmöglich.
Dafür greift der Vorschlag von Rickals mit einem ständig messenden (und nicht schaltenden) Shelly - der PM Mini. Dieser stellt dann fest, wenn ein Verbraucher als Master eingeschaltet wird und informiert schaltende Shelly.
-
Meine Versuche zur Einbindung von Shelly BLU Geräten wie BLU H&T, BLU Door/Window via WebUI unter Components -> Bluetooth (BTHome) devices shlägt wiederholt fehl. Bereits der Scan Button unter "Add BTHome (Bluetooth) devices" ist grau unterlegt.
Auch der Weg über "Add a device by MAC address" schlägt fehl.
Installierte Firmware: 1.8.99-pillprod0 oder die Beta Version vom 2025-12-22
An der Hardware dürfte es nicht liegen, weil Tests mit meinen Skripten zu Gen 2 Shelly erfolgreich verlaufen.
Gibt es dazu Erkenntnisse/Informationen?
-
Ich kenne HomeAssistant nicht. Jedenfalls unterstützt ein Shelly der Generation 2, wie die Plus Geräte, keine Einbindung von BLU Shelly via Firmware, was auch bereits festgestellt wurde. So etwas gelingt mit einem bzw. zwei Skripten, indem man bspw. MQTT Nachrichten nutzt. as HA mit MQTT kann oder nicht kann, weiß ich nicht. Ich kriege so etwas jedenfalls mit Skripten und Node-RED hin.
Mit einem Gen 3+ Shelly wirst du weitere Optionen haben.
Habe den BLU D/W in den Bluetooth Einstellungen des Shelly Plus 1 eingefügt
Afaik ist so etwas bisher nicht möglich.
-
Btw, ich habe soeben eine Skripte Lösung für energiesparenden Betrieb dokumentiert - standalone. Dies entspricht zwar nicht den Vorgaben des TE, könnte aber als Alternative interessant sein - nur als wohlwollendes Angebot und Grundlage für diesen Einsatzzweck. Vielleicht trifft es auf Akzeptanz.
Dies ist zu finden unter BLU Daten per Shelly Gen 3 und MQTT senden - ohne oder mit Skript (<- auch Gen 2).
-
Mein Fortschritt in der Nutzung von BLU Daten mit einem Shelly Plus (Gen 2)
Dies ließe sich entsprechend auf ein Gen 3+ Gerät portieren, was ich noch nicht tat, weil mein einziges Gen 3 Gerät ausfiel.
Es werden drei Skripte zu Grunde gelegt.
- "ble-shelly-blu.js" als bereits vorliegendes Skript zur Erkennung von Shelly BLU Geräten
Dieses emittiert Events, welche von meinem folgenden Skript verarbeitet werden. - Mein Skript "ble.js" verarbeitet Nachrichten, die von im Skript eingetragenen BLU Geräten gesendet werden:
Derzeit habe ich folgende Geräte erfolgreich eingesetzt/getestet.
BLU H&T, BLU Motion, BLU Door/Window und BLU Button
Dieses Skript emittiert selbst bei Bedarf Events, welche bspw. von einem dritten Skript verarbeitet werden.
Bei Bedarf sendet es eine Webseite, auf welcher die BLU Daten in Tabellenform dargestellt werden. - Ein zusätzliches Skript "process.js" dient ausschließlich dazu, bei Bedarf auf Grund von BLU Nachrichten gewünschte Prozesse zu starten.
Bisher gibt es ausschließlich von "ble.js" emittierte Ereignisse aus. Es stellt nur ein Grundgerüst bereit und ist nach Bedarf auszubauen.
Diese Aufteilung bietet relativ übersichtlich alle Optionen, welche ein Anwender ggf. wünscht.
Zwecks anwendungsorientierter Anpassung ist in "ble.js" die Konfiguration anzupassen und zwecks Verarbeitung "process.js" zu ergänzen.
Mit den von mir bisher getesteten BLU Geräten lässt sich sehr viel implementieren, solange die Hardware dazu geeignet ist.
Ein Beispiel zu einer Heizungsregelung - evtl. in einem Wohnmobil
- In "ble.js" oder in "process.js" oder einem weiteren Skript wird die eigentliche Regelung implementiert.
- Die Daten werden von "ble.js" via Webseite visualisiert.
- Mit einem BLU Button kann die Zieltemperatur verändert werden, bspw. einmal drücken -> verringern, zweimal drücken -> erhöhen, lange drücken -> Voreinstellung. Dies wird von "process.js" verarbeitet und kann bei Bedarf via Smartphone und Browser beobachtet werden.
- BLU Door/Window meldet offene(s) Fenster, woraufhin die Zieltemperatur temporär gesenkt wird.
- Wenn längere Dauer keine Bewegung via BLU Motion(s) gesendet wird, wird die Zieltemperatur gesenkt bzw. umgekehrt nach mehreren Bewegungserkennungen wird die Zieltemperatur auf einen vorgesehenen Wert gestellt.
Der Vorteil einer solchen Realisierung liegt im geringen Leistungsbedarf, was für Wohnmobil/Wohnwagen besonders nützlich erscheint. Ein Smartphone ist i.d.R. ohnehin an Bord und via Hotspot bestens zu Zeitsynchronisation per NTP des Skript Shelly geeignet. Solange dieser Shelly keinen Neustart ausführt, bleibt die einmal synchronisierte Zeit weitestgehend aktuell. Das Smartphone ist aber auch danach als Hotspot geeignet, um die Webseite des Shelly via IP Adresse abzurufen. Dafür muss nicht der Shelly AP genutzt werden, auch wenn dies ebenso möglich ist.
Die Skripte
ble.js mit fünf konfigurierten BLU Geräten
Die Daten eines noch nicht konfigurierten BLU Gerätes wird, alternativ zur App "Shelly BLE Debug", in der Console ausgegeben, wo u.a. die Adresse zu sehen ist. Dies erfolgt, wenn GetNew auf true gesetzt ist.JavaScript
Alles anzeigen// Created by eiche, version 2026-01-12_01 // This script requires the running script ble-shelly-blu.js. const PreTopic = "Wohnmobil/", // the MQTT pretopic Device = "Wohnmobil", // the device name Debug = false, GetNew = true, // If true, all devices not listed in BLEdev will be displayed in the console. MotVal = ["Nein", "Ja"], // motion values WinVal = ["geschlossen", "offen"], // window values EventName = "ble", // event name of the event to be emitted by function proc ValidEvent = "shelly-blu"; // the only event name, that will be handled by this script // This function can be used by a BLE event. For this to work, "proc" must be added to the end of a BLEdev.comp entry. // The emitted event may also be handled by another script. function proc(obj) { Shelly.emitEvent(EventName, obj); } // The list of all device messages to be processed. // addr = address of the BLE devive // topic = the MQTT topic added to the PreTopic value // name = name of the BLE device // comp = list of the interested sensor values of the device // If you change the following category names, you should do so in the function getComp() and in the <style> sequence under httpd. // bat = battery, but = button, hum = humidity, ill = illuminance, mot = motion, rot = rotation, tmp = temperature, win = window const BLEdev = [ // The components (comp) will be stored in Data just in the same order like in this list. // Finally, you should remove all of these example entries. {addr: "7c:c6:b6:04:4b:0f", topic: "WoMoHT1", name: "WoMoHT1", comp: [// type, name, unit and optional a processing function ['tmp', "Temperatur", "°C", proc], ['hum', "Luftfeuchtigkeit", "%"], ['bat', "Batterie", "%"] ] }, {addr: "7c:c6:b6:96:85:85", topic: "WoMoHT2", name: "WoMoHT2", comp: [// type, name, unit ['tmp', "Temperatur", "°C"], ['hum', "Luftfeuchtigkeit", "%"], ['bat', "Batterie", "%"] ] }, {addr:"b0:c7:de:40:72:46", topic: "Motion", name: "Motion", comp: [ ['mot', "Bewegung", "", proc], ['ill', "Helligkeit", "lx"], ['bat', "Batterie", "%"] ] }, {addr:"94:b2:16:2b:31:ba", topic: "Fenster", name: "Fenster", comp: [ ['win', "Zustand", "", proc], ['rot', "Neigung", "°"], ['ill', "Helligkeit", "lx"], ['bat', "Batterie", "%"] ] }, {addr:"5c:c7:c1:f3:ea:5e", topic: "Button", name: "Button", comp: [ ['but', "gedrueckt", "mal", proc], ['bat', "Batterie", "%"] ] } ]; // end of configuration let Data = []; function procComp(info, obj, offset) { //if(typeof obj.comp[0] !== "string") return info; let data = info.data; //print(JSON.stringify(data)); let res = {}; try { let val, cat, l = obj.comp.length; for(let i = 0; i<l; ++i) { val = null; cat = obj.comp[i][0]; //print(data); switch(cat) { case 'bat': res.battery = data.battery; val = data.battery; break; case 'but': res.button = data.button; val = data.button; break; case 'hum': res.humidity = data.humidity; val = data.humidity; break; case 'ill': res.illuminance = data.illuminance; val = data.illuminance; break; case 'mot': res.motion = data.motion; val = MotVal[data.motion]; break; case 'rot': res.rotation = data.rotation; val = data.rotation; break; case 'tmp': res.temperature = data.temperature; val = data.temperature; break; case 'win': res.window = data.window; val = WinVal[data.window]; break; default: console.log("component shortcut unknown or not yet implemented:", obj.comp[i]); } if(val!==null) { Data[offset+i] = {name:obj.name+' '+obj.comp[i][1], val:val, unit:obj.comp[i][2], cat:cat, ts:Math.round(info.ts)}; let f = obj.comp[i][3]; if(f!==undefined && f!==null) f(Data[offset+i]); } } } catch(err) {console.log(err);} return res; } function process(ev) { if(ev.info===undefined || ev.info.event!==ValidEvent) return; let i, info = ev.info, l = BLEdev.length, offset = 0; //print(JSON.stringify(ev)); for(i=0; i<l && BLEdev[i].addr!==info.data.address; ++i) offset += BLEdev[i].comp.length; if(i>=l) { if(GetNew) print(JSON.stringify(info)); return; } //print(JSON.stringify(info.data)); let p = procComp(info, BLEdev[i], offset); if(p==={}) return; p.name = BLEdev[i].name; p.ts = Math.round(info.ts); p = JSON.stringify(p); if(Debug) print(p); //MQTT.publish(PreTopic + BLEdev[i].topic, p); } Shelly.addEventHandler(process); // --- utilities function d2(d) { d = JSON.stringify(d); return d.length<2 ? '0' + d : d; } function getTime(t) { if(t===null) return; let d; if(t===undefined) d = new Date(); else d = new Date(t); return d.getFullYear()+'-'+d2(JSON.parse(d.getMonth()) + 1)+'-'+d2(d.getDate()) +' '+d2(d.getHours())+':'+d2(d.getMinutes())+':'+d2(d.getSeconds()); } // --- end of utilities // --- httpd --- let Mode = [ // e.g. three refresh modes, you may try one mode ;-P {name:"ohne Ladezyklen", value:0}, // without refreshing {name:"alle 60s", value:60}, // regular mode {name:"alle 30s", value:30} // one of possible multiple alternatives ], m = 0, // default or last mode Refresh = 60, // refresh time in seconds tTup = ' ∆ ', tTdn = ' ∇ ', Dis = '☐', En = '☑', dataURL = ''; let localIP = Shelly.getComponentStatus('wifi').sta_ip, htmlFirst = '<!DOCTYPE html>' +'<html>' +'<head>' +'<meta name="viewport" content="width=device-width, initial-scale=1.0">', htmlSecond = '<title>'+Device+'</title>' +'</head>' +'<style>' +'body,table {background-color:#404040;color:white;font-family:sans-serif;text-align:center;font-size:0.9em}' +'a {color:yellow;text-decoration:none;font-weight:bold}' +'table {table-layout:auto;width:100%}' +'table,th,td {border:1px solid white;border-collapse:collapse;padding:2px}' +'td,th {text-align:center}' +'.dev,.tt,.tmp,.hum,.mot, .ill, .out {font-weight:bold}' +'.dev, .out, .win {font-size:1.5em}' +'.tt, .tmp, .hum, .bat, .mot, .ill, .rot, .but {font-size:2em}' +'.tt {color:white}' +'.tmp, .ill {color:#ffd000}' +'.hum {color:#00d0ff}' +'.bat {color:#ffffff}' +'.sm {font-size:0.8em}' +'.dis {color:red}' //+'body {display: flex; align-items: center; justify-content: center; height: 100vh;}' +'label {font-size: 1em}' +'#i {width:2em}' +'#t {width:5.5em;font-size:1.3em}' +'#tT {width:3em}' +'#i, #tT {font-size:1em}' +'button {font-size:1em}' +'.in {padding:8px}' +'</style>' +'<body>', htmlLast = '</body></html>'; function data(request,response) { response.code = 200; response.headers = [["Content-Type", "text/html"]]; if(request.method==='GET') { let q = request.query, p = [], wait = "1"; //print(q); if(q.length>0) { let a = q.indexOf('&'); if(a>0 && a<q.length-1) { q = q.split('&'); if(!isNaN(q[0])) m = Math.abs(JSON.parse(q[0])) % Mode.length; q = q[1]; } p = q.split('='); if(p.length==2 && !isNaN(p[1])) { let v = JSON.parse(p[1]); switch(p[0]) { case 'm': m = Math.abs(v) % Mode.length; Refresh = Mode[m].value; break; case 'r': Refresh = Math.abs(v); break; } } } } else if(request.method==='POST') { print(request.body); } let n = (m+1) % Mode.length, r = Refresh; response.body = htmlFirst +(r>0 ? ('<meta http-equiv="refresh" content="'+r.toString()+'">') : '') +htmlSecond +'<table>' +'<tr><th class="dev" colspan="2">'+Device+'</th><th class="sm">'+getTime()+'</th></tr>' +'<tr><th>Name</th><th>Messwert</th><th>Zeitpunkt</th></tr>'; for(let i = 0; i<Data.length; ++i) { let o = Data[i]; if(o!==undefined && o!==null) { response.body += '<tr><td>'+o.name+'</td><td class="'+o.cat+'">'+o.val+' '+o.unit +'</td><td class="sm">'+getTime(o.ts*1000)+'</td></tr>'; } } response.body += '</table>'+htmlLast; response.send(); } Timer.set(5000,false, function(){ dataURL = 'http://'+localIP + HTTPServer.registerEndpoint('data', data); print(dataURL,'? r=refresh time | m=mode'); } );ble-shelly-blu.js auch leicht im WWW zu finden. Hier eine etwas komprimierte Fassung.
JavaScript
Alles anzeigen// origin script: ble-shelly-blu.js // compressed by eiche without changing functionality on 2025-06-04 /** * This script will use BLE scanner to listen for advertising data from nearby Shelly BLU devices, * decodes the data using a BTHome data structure, and emits the decoded data for further processing. * * This script DOESN'T execute actions, only emit events. Can be used with `ble-events-handler.js` example. * You can configure the event name, by default its `shelly-blu`, the body of the event contains all the data * parsed from the BLE device * * Represents data provided by each device. * Every value illustrating a sensor reading (e.g., button) may be a singular sensor value or * an array of values if the object has multiple instances. * * @typedef {Object} DeviceData * @property {number} pid - Packet ID. * @property {number} battery - The battery level of the device in percentage (%). * @property {number} rssi - The signal strength in decibels (dB). * @property {string} address - The MAC address of the Shelly BLU device. * @property {string} model - The model of the Shelly BLU device. * @property {number | number[]} [temperature] - The temperature value in degrees Celsius if the device has a temperature sensor. (Can be an array if has multiple instances) * @property {number | number[]} [humidity] - The humidity value in percentage (%) if the device has a humidity sensor. (Can be an array if has multiple instances) * @property {number | number[]} [illuminance] - The illuminance value in lux if the device has a light sensor. (Can be an array if has multiple instances) * @property {number | number[]} [motion] - Motion status: 0 for clear, 1 for motion (if the device has a motion sensor). (Can be an array if has multiple instances) * @property {number | number[]} [window] - Window status: 0 for closed, 1 for open (if the device has a reed switch). (Can be an array if has multiple instances) * @property {number | number[]} [button] - The number of presses if the device has a button. (Can be an array if has multiple instances) * @property {number | number[]} [rotation] - The angle of rotation in degrees if the device has a gyroscope. (Can be an array if has multiple instances) * * @example * {"component":"script:*","name":"script","id":*,"now":*,"info":{"component":"script:*","id":*,"event":"shelly-blu","data":{"encryption":false,"BTHome_version":2,"pid":118,"battery":100,"button":1,"rssi":-76,"address":*},"ts":*}} */ /******************* START CHANGE HERE *******************/ const CONFIG = { // Specify the destination event where the decoded BLE data will be emitted. It allows for easy identification by other applications/scripts eventName: "shelly-blu", // If this value is set to true, the scan will be active. // If this value is set to false, the scan will be passive. // Active scan means the scanner will ping back the Bluetooth device to receive all its data, but it will drain the battery faster active: false, // When set to true, debug messages will be logged to the console debug: true, }; /******************* STOP CHANGE HERE *******************/ const BTHOME_SVC_ID_STR = "fcd2"; const uint8 = 0; const int8 = 1; const uint16 = 2; const int16 = 3; const uint24 = 4; const int24 = 5; // The BTH object defines the structure of the BTHome data const BTH = { 0x00: { n: "pid", t: uint8 }, 0x01: { n: "battery", t: uint8, u: "%" }, 0x02: { n: "temperature", t: int16, f: 0.01, u: "tC" }, 0x03: { n: "humidity", t: uint16, f: 0.01, u: "%" }, 0x05: { n: "illuminance", t: uint24, f: 0.01 }, 0x21: { n: "motion", t: uint8 }, 0x2d: { n: "window", t: uint8 }, 0x2e: { n: "humidity", t: uint8, u: "%" }, 0x3a: { n: "button", t: uint8 }, 0x3f: { n: "rotation", t: int16, f: 0.1 }, 0x45: { n: "temperature", t: int16, f: 0.1, u: "tC" }, }; function getByteSize(type) { if (type === uint8 || type === int8) return 1; if (type === uint16 || type === int16) return 2; if (type === uint24 || type === int24) return 3; //impossible as advertisements are much smaller; return 255; } // functions for decoding and unpacking the service data from Shelly BLU devices const BTHomeDecoder = { utoi: function (num, bitsz) { const mask = 1 << (bitsz - 1); return num & mask ? num - (1 << bitsz) : num; }, getUInt8: function (buffer) {return buffer.at(0);}, getInt8: function (buffer) {return this.utoi(this.getUInt8(buffer), 8);}, getUInt16LE: function (buffer) {return 0xffff & ((buffer.at(1) << 8) | buffer.at(0));}, getInt16LE: function (buffer) {return this.utoi(this.getUInt16LE(buffer), 16);}, getUInt24LE: function (buffer) {return (0x00ffffff & ((buffer.at(2) << 16) | (buffer.at(1) << 8) | buffer.at(0)));}, getInt24LE: function (buffer) {return this.utoi(this.getUInt24LE(buffer), 24);}, getBufValue: function (type, buffer) { if (buffer.length < getByteSize(type)) return null; let res = null; if (type === uint8) res = this.getUInt8(buffer); if (type === int8) res = this.getInt8(buffer); if (type === uint16) res = this.getUInt16LE(buffer); if (type === int16) res = this.getInt16LE(buffer); if (type === uint24) res = this.getUInt24LE(buffer); if (type === int24) res = this.getInt24LE(buffer); return res; }, // Unpacks the service data buffer from a Shelly BLU device unpack: function (buffer) { //beacons might not provide BTH service data if (typeof buffer !== "string" || buffer.length === 0) return null; let result = {}; let _dib = buffer.at(0); result["encryption"] = _dib & 0x1 ? true : false; result["BTHome_version"] = _dib >> 5; if (result["BTHome_version"] !== 2) return null; //can not handle encrypted data if (result["encryption"]) return result; buffer = buffer.slice(1); let _bth; let _value; while (buffer.length > 0) { _bth = BTH[buffer.at(0)]; if (typeof _bth === "undefined") { console.log("BTH: Unknown type"); break; } buffer = buffer.slice(1); _value = this.getBufValue(_bth.t, buffer); if (_value === null) break; if (typeof _bth.f !== "undefined") _value = _value * _bth.f; if (typeof result[_bth.n] === "undefined") result[_bth.n] = _value; else { if (Array.isArray(result[_bth.n])) result[_bth.n].push(_value); else result[_bth.n] = [result[_bth.n], _value]; } buffer = buffer.slice(getByteSize(_bth.t)); } return result; }, }; /** * Еmitting the decoded BLE data to a specified event. It allows other scripts to receive and process the emitted data * @param {DeviceData} data */ function emitData(data) { if (typeof data !== "object") return; Shelly.emitEvent(CONFIG.eventName, data); } //saving the id of the last packet, this is used to filter the duplicated packets let lastPacketId = 0x100; // Callback for the BLE scanner object function BLEScanCallback(event, result) { //exit if not a result of a scan if (event !== BLE.Scanner.SCAN_RESULT) return; //exit if service_data member is missing if (typeof result.service_data === "undefined" || typeof result.service_data[BTHOME_SVC_ID_STR] === "undefined") return; let unpackedData = BTHomeDecoder.unpack(result.service_data[BTHOME_SVC_ID_STR]); //exit if unpacked data is null or the device is encrypted if (unpackedData === null || typeof unpackedData === "undefined" || unpackedData["encryption"]) { console.log("Error: Encrypted devices are not supported"); return; } //exit if the event is duplicated if (lastPacketId === unpackedData.pid) return; lastPacketId = unpackedData.pid; unpackedData.rssi = result.rssi; unpackedData.address = result.addr; unpackedData.model = result.local_name; emitData(unpackedData); } // Initializes the script and performs the necessary checks and configurations function init() { //exit if can't find the config if (typeof CONFIG === "undefined") { console.log("Error: Undefined config"); return; } //get the config of ble component const BLEConfig = Shelly.getComponentConfig("ble"); //exit if the BLE isn't enabled if (!BLEConfig.enable) { console.log("Error: The Bluetooth is not enabled, please enable it from settings"); return; } //check if the scanner is already running if (BLE.Scanner.isRunning()) console.log("Info: The BLE gateway is running, the BLE scan configuration is managed by the device"); else { //start the scanner const bleScanner = BLE.Scanner.Start({duration_ms: BLE.Scanner.INFINITE_SCAN, active: CONFIG.active}); if (!bleScanner) console.log("Error: Can not start new scanner"); } //subscribe a callback to BLE scanner BLE.Scanner.Subscribe(BLEScanCallback); // disable console.log when logs are disabled if (!CONFIG.debug) console.log = function () { }; } init();process.js ist nur eine minimale Bearbeitungsgrundlage, noch ohne echte Funktionalität. Hier werden nur Daten ausgegeben.
JavaScriptfunction process(ev) { if(ev.info.event!=="ble") return; //print(JSON.stringify(ev)); let d = ev.info.data; print(d.name, d.val); } Shelly.addEventHandler(process);Der Übersichtlichkeit wegen sollte die Prozessfunktionalität in diesem Skript untergebracht werden. Es ist durchaus auch möglich, diese Funktionalität im Skript "ble.js" zu platzieren und die Webseitengenerierung in ein anderes Skript auszulagern. Bei Ausweitung eines solchen Projektes wird man sehen, was günstiger erscheint.
Screenshot der Webseite
Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen. - "ble-shelly-blu.js" als bereits vorliegendes Skript zur Erkennung von Shelly BLU Geräten
-
Dazu eine autarke Lösung mit einem Shelly Plus Uni oder einem Shelly 2PM Gen x und einem Skript:
- Zwei Taster an beiden Eingängen
- Ein Ausgang schaltet (indirekt) den Verbraucher.
- Der andere Ausgang lässt eine LED kurz blinken
1 x blink -> Zieltemperatur runter
2 x blink -> Zieltemperatur rauf
Dazu liefert das Skript eine Webseite mit den gewünschten Informationen.
Etwas ähnliches habe ich mit einem Shelly Plus 1 und Addon realisiert. Es funktioniert für mich bestens und verlässlich.
-
Kleine Korrektur:
Mit einem Shelly der zweiten Generation und bspw. zwei Skripten geht das auch, wobei ein Skript bereits öffentlich und fertig vorliegt.
Mit einem Gen 3+ geht es allerdings etwas bequemer.
-
Ein Shelly 1(PM) hat immer nur einen Schaltausgang (potentialfrei). Dafür steht die 1. Wenn immer eines der Ventile geöffnet sein soll, kannst du für das andere Ventil zusätzlich ein externes Relais verwenden. Oder du nimmst einen Shelly 2PM - zwei Ausgänge, aber nicht potentialfrei. Mit Letzterem kannst du auch beide Ventile schließen lassen.
-
Die meiner Ansicht nach beste Lösung ist ein Shelly Skript auf deinem 1PM Gen 4, welches neben der Regelung auch eine Webseite liefert. Darauf Symbole zum erhöhen/absenken der Zieltemperatur. Auch Zeitpläne sind bei Bedarf möglich. Das Einbinden eines BLU H&T statt eines kabelgebundenen Sensors ist auch kein Problem.
So wird keine zusätzliche Ausstattung gebraucht, weil der schaltende Shelly völlig autark arbeitet. Was nicht gebraucht wird, kann auch nicht ausfallen bzww. dessen Ausfall wirkt sich nicht auf die Funktion der Heizungsregelung aus. Mit ioBroker kenne ich mich nicht aus.
Die Verbindung mit dem 1PM kann auch über dessen Accesspoint laufen.
-
Wo und wie stellst du dir die Anzeige des BLU Door/Window Zustandes vor - in der App, Cloud oder WebUI (gelingt mit Gen 2 nicht) oder via eigener Webseite vom Plus 1?
Eine Webseite kann per Skript geliefert werden, darin auch Daten/Zustände von BLU Geräten/Sensoren. Ein solches Skript könnte ich beisteuern. Wie so etwas prinzipiell aussehen kann, kannst du hier sehen. Statt Text sollte sich auch etwas per Symbolen oder Bildern machen lassen, was ich bisher noch nicht brauchte.
-
Ergänzung zur Machbarkeit - auch auf Basis der Schaltung von thgoebel in #25
Zur Rollladensteuerung mit teilweiser Öffnung (%) muss der 2PM kalibriert werden. Letzteres wird vermutlich nicht gelingen. Wenn nach dem Motto "Nichts ist unmöglich" nach einer Lösung gesucht werden soll, bietet sich allenfalls Aufwändiges an.
- Tasmota auf einem ESP32 Board (Bsp.) mit manueller Kalibrierung
- Shelly Skript mit experimenteller Zeiterfassung und Konfiguration im Skript oder im Key Value Storage (KVS). Dazu müsste in gewissen Abständen eine Referenzstellung (Tor geschlossen oder komplett offen) angefahren werden.
Dies sei nur der Vollständigkeit halber erwähnt.
-
-
Für energiesparenden Einsatz: Shelly Gen 3+ liefert BLU Daten auf einer Webseite - keine Action erforderlich
Konfiguration ausschließlich via Sensor (Messwerte) Nameneinträge, kein Eingriff in das Skript erforderlich
Ich arbeite gelegentlich an einer Bluetooth Sensorabfrage ohne übergeordnetes System und ohne Cloud.
Grund: Dies soll energiesparend unterwegs einsetzbar sein - bspw. in einem Wohnmobil oder einem Wohnwagen.
Das folgende Skript ist weiterhin in Bearbeitung aber bereits nutzbar. Es generiert eine Webseite mit Daten von Shelly Bluetooth Sensoren. Somit können diese Daten per Smartphone dargestellt und beobachtet werden. Fernes Ziel ist, darin eine digitale Heizungsregelung zu implementieren.
Ostfriese (Nickname) hatte eine sehr gute Lösung für Ruuvi Sensoren bereits implementiert. Ich verfolge hier eine reine Shelly Lösung. Dafür setze ich gegenwärtig ein ModX1 Entwicklungsboard ein, welches derzeit und bereits länger nicht mehr angeboten wird. Auch ein Shelly Plus Uni kann verwendet werden, erfordert dann aber ein zusätzliches Skript sowie Änderungen im untenstehenden Skript.
Die Webseite wird durch die Namen der darzustellenden Sensordaten konfiguriert.
Die zugrunde liegende Struktur jedes Namens: name:unit:index:category
Als Separator der Namesteile dient der Doppelpunkt und kann bei Bedarf anders gewählt werden.Die Webseite zeigt in Tabellenform jeden aufzulistenden Eintrag.
Eine Tabellenzeile besteht aus name, Messwert mit unit, Zeitpunkt der letzten Messwerterfassung.
index gibt die Zeilenposition innerhalb der Tabelle an und sollte mit 0 beginnen.
Derzeit wird category zur Darstellung der Messwerte verwendet (Farbe und Schriftgröße).
Bisher verwendet das Skript die Kategorienwerte tmp (Temperatur), hum (Luftfeuchtigkeit) und bat (Batteriezustand).Im Tabellenkopf wird u.a. Zeitpunkt der letzten Webseitenaktualisierung angezeigt. Über diese Zeitangaben kann leicht die Aktualität der Daten beobachtet werden.
Die Webseite wird standardmäßig alle 60s neu geladen. Dieser Refreshwert kann bei Bedarf per ?r=<Sekunden> gewählt werden.
Nach dem Start des Skripts wird in der Console der vorgesehene URL angezeigt.Ein Screenshot zeigt den Aufbau der Webseite. Diese ist auch per Shelly Access Point erreichbar (192.168.33.1), also ohne Zusatzhardware.
Der Webseite URL lautet http://<IP-Adresse>/script/1/data, wenn das Skript die Id 1 besitzt.Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen. Die Temperaturunterschiede ergeben sich aus den unterschiedlichen Platzierungen der Shelly BLU H&T in unserem Wohnmobil - wir sind derzeit unterwegs.
Beispiel der Konfiguration eines Sensors auf dem Shelly WebUI unter Components/Bluetooth (BTHome) devices/Supported sensors:
Der Inhalt kann nicht angezeigt werden, da Sie keine Berechtigung haben, diesen Inhalt zu sehen.
Der Batteriezustand wird auf der Webseite in der dritten Tabellenzeile in % angezeigt (hier wäre wohl statt % der HTML Code besser).
Diese Art der Konfiguration via Name ist ohne Skripteingriff nutzbar, aber nicht DAU sicher. So wird ein mehrfach vergebener gleicher index Wert dazu führen, dass immer der letzte assoziierte Datensatz an der indizierten Stelle platziert wird. Welche Folge ein (überflüssigerweise) sehr hoch gewählter index Wert hat, vermag ich nicht zu sagen. Ein vorgesehener Abstand von 5 (oder gar 10) zwischen zwei Shelly BLU Geräten mag nützlich sein, um später leichter Sensordaten eines weiteren Gerätes einfügen zu können. Dies habe ich bisher allerdings nicht getestet.Das Skript empfängt ohne Action die Sensordaten, speichert diese und die aufgerufene Webseite stellt die gespeicherten Daten asynchron dar.
Wenn die Zeitpunkte mangels Zeitserver Verfügbarkeit nicht brauchbar sind, kann für max. 1 Minute ein Smartphone via Hotspot die Zeitsynchronisation des Shelly Gen3+ zur Verfügung stellen. Im Skript stehen noch einige Teile, die ggf. nicht gebraucht werden. Dies muss zukünftig die Robustheit des Skripts entscheiden. Code Verbesserungen sind zukünftig möglich.Bei Fragen will ich gerne zur Verfügung stehen.
JavaScript
Alles anzeigen// created by eiche, 2026-01-06 // This script, running on a Shelly generation 3 or higher, may sends MQTT messages with data from all BTHome sensor values // of a BTHome device. // This happens immediately after the script receives a status message from a BTHome device. Nothing further needs to be done. // The function bths() requires a composition of sensor name and optional unit in the component name. // Sensor name should contain following parts separated by a separator containing the variable Sep, e.g. a colon. // name:unit:index:category // index specifies the position of the memory within the data array and represents the table row on the generated web page. // In function bths you may do something else with the sensor data, e.g. use the "HTTP.Get" method with an URL. // But that method can be in an action and does not need a script. // Have fun with it anyway! // Additionally a web page is send after an http reequest - look at comment "httpd". //not yet implemented respective in use: Additionally, puschRPC() stores incoming messages to process them at specific times. // A small configuration let debug = false; let Device = "Wohnmobil"; let Topic = "versuch/bthome", // the MQTT topic Sep = ':', // the seperator symbol - it may be a string instead of one character // The following two entries may be obsolete. Dt = 2000, // delay milliseconds before each call of function next Max = 10; // maximum of RPC entries // end of configuration let Data = []; function store(obj) { // stores object into Data if(obj===undefined || obj===null) { console.log("push() invalid value:", obj); return; } if(debug) print(JSON.stringify(obj)); let i = obj.ix; if(i!==undefined && i!==null) Data[i] = obj; } // under construction to avoid script abort - this may be obsolete let RPC = [], Th = null; function next() { // process next queued message if(RPC.length===0) {Timer.clear(Th); return;} let msg = RPC.pop(); //print(msg); msg = JSON.parse(msg); Shelly.call("http.get", {url:"http://"+msg.target+"/rpc/"+msg.method+(msg.param===undefined?"":msg.param)}, function(res, errc, errm, msg) { if(errc) {console.log("next", errc, errm); return;} //print(res.body); let val = JSON.parse(res.body.split('"'+msg.comp+'":')[1]); let nu = msg.name.split(Sep); print(nu[0], val, (nu[1]===undefined?"":nu[1])); }, msg ); } function pushRPC(msg) { // stores msg in a queue named RPC if(msg===undefined || msg===null || msg.target===undefined || msg.method===undefined) { console.log("push() invalid parameter:", msg); return; } //print(JSON.stringify(msg)); msg = JSON.stringify(msg); if(RPC.length>=Max) {console.log("RPC.push overfow"); return;} let i = RPC.indexOf(msg); if(i<0) RPC.push(msg) else RPC[i] = msg; // place the newest message into RPC } function procRPC() { // start processing the queued messages Th = Timer.set(Dt, true, next); } // end of under construction to avoid script abort // Get BTHome sensor data and do something, e.g. send those via MQTT. // to do: Create sensor names in following structure! A colon, for example, can be used as a separator. // name:unit:index:category // e.g. "WoMoHT2 Temperatur:°C:3:tmp" function bths(id) { //print(id); Shelly.call("bthomesensor.getstatus", {id:id}, function(res, errc, errm) { if(errc) {console.log(errc, errm); return;} //print(JSON.stringify(res)); let Sensor = {id:res.id, value:res.value, ts:res.last_updated_ts, name:"", unit:"", ix:null, cat:null}; Shelly.call("bthomesensor.getconfig", {id:id}, function(res, errc, errm, s) { if(errc) {console.log(errc, errm); return;} //print(JSON.stringify(res)); let n = res.name.split(Sep); let l = n.length; if(l>0) s.name = n[0]; if(l>1) s.unit = n[1]; if(l>2) s.ix = n[2]; if(l>3) s.cat = n[3]; // store at Values store(s); let p = JSON.stringify(s); if(debug) print(p); //MQTT.publish(Topic, p); // or do something else with object s respective Sensor }, Sensor); }); } function bthStatus(st) { if(st.name!=="bthomedevice") return; //print(JSON.stringify(st)); Shelly.call("BTHomeDevice.GetKnownObjects", {id:st.id}, function(res, errc, errm) { if(errc) {console.log(errc, errm); return;} //print(JSON.stringify(res)); for(let i=res.objects.length-1; i>=0; --i) { //print(res.objects[i].component); if(res.objects[i].component!==null) bths(res.objects[i].component.split(':')[1]); } procRPC(); }); } Shelly.addStatusHandler(bthStatus); // --- utilities function d2(d) { d = JSON.stringify(d); return d.length<2 ? '0' + d : d; } function getTime(t) { if(t===null) return; let d; if(t===undefined) d = new Date(); else d = new Date(t); return d.getFullYear()+'-'+d2(JSON.parse(d.getMonth()) + 1)+'-'+d2(d.getDate()) +' '+d2(d.getHours())+':'+d2(d.getMinutes())+':'+d2(d.getSeconds()); } // --- end of utilities // --- httpd --- let Mode = [ // e.g. three refresh modes, you may try one mode ;-P {name:"ohne Ladezyklen", value:0}, // without refreshing {name:"alle 60s", value:60}, // regular mode {name:"alle 30s", value:30} // one of possible multiple alternatives ], m = 0, // default or last mode Refresh = 60, // refresh time in seconds tTup = ' ∆ ', tTdn = ' ∇ ', Dis = '☐', En = '☑', dataURL = ''; let localIP = Shelly.getComponentStatus('wifi').sta_ip, htmlFirst = '<!DOCTYPE html>' +'<html>' +'<head>' +'<meta name="viewport" content="width=device-width, initial-scale=1.0">', htmlSecond = '<title>'+Device+'</title>' +'</head>' +'<style>' +'body,table {background-color:#404040;color:white;font-family:sans-serif;text-align:center;font-size:0.9em}' +'a {color:yellow;text-decoration:none;font-weight:bold}' +'table {table-layout:auto;width:100%}' +'table,th,td {border:1px solid white;border-collapse:collapse;padding:2px}' +'td,th {text-align:center}' +'.dev,.tt,.tmp,.hum,.out {font-weight:bold}' +'.dev, .out {font-size:1.5em}' +'.tt, .tmp, .hum, .bat {font-size:2em}' +'.tt {color:white}' +'.tmp {color:#ffd000}' +'.hum {color:#00d0ff}' +'.bat {color:#ffffff}' +'.sm {font-size:0.8em}' +'.dis {color:red}' //+'body {display: flex; align-items: center; justify-content: center; height: 100vh;}' +'label {font-size: 1em}' +'#i {width:2em}' +'#t {width:5.5em;font-size:1.3em}' +'#tT {width:3em}' +'#i, #tT {font-size:1em}' +'button {font-size:1em}' +'.in {padding:8px}' +'</style>' +'<body>', htmlLast = '</body></html>'; function data(request,response) { response.code = 200; response.headers = [["Content-Type", "text/html"]]; if(request.method==='GET') { let q = request.query, p = [], wait = "1"; //print(q); if(q.length>0) { let a = q.indexOf('&'); if(a>0 && a<q.length-1) { q = q.split('&'); if(!isNaN(q[0])) m = Math.abs(JSON.parse(q[0])) % Mode.length; q = q[1]; } p = q.split('='); if(p.length==2 && !isNaN(p[1])) { let v = JSON.parse(p[1]); switch(p[0]) { case 'm': m = Math.abs(v) % Mode.length; Refresh = Mode[m].value; break; case 'r': Refresh = Math.abs(v); break; } } } } else if(request.method==='POST') { print(request.body); } let n = (m+1) % Mode.length, r = Refresh; response.body = htmlFirst +(r>0 ? ('<meta http-equiv="refresh" content="'+r.toString()+'">') : '') +htmlSecond +'<table>' +'<tr><th class="dev" colspan="2">'+Device+'</th><th class="sm">'+getTime()+'</th></tr>' +'<tr><th>Name</th><th>Messwert</th><th>Zeitpunkt</th></tr>'; for(let i = 0; i<Data.length; ++i) { let o = Data[i]; if(o!==undefined && o!==null) { response.body += '<tr><td>'+o.name+'</td><td class="'+o.cat+'">'+o.value+' '+o.unit +'</td><td class="sm">'+getTime(o.ts*1000)+'</td></tr>'; } } response.body += '</table>'+htmlLast; response.send(); } Timer.set(5000,false, function(){ dataURL = 'http://'+localIP + HTTPServer.registerEndpoint('data', data); print(dataURL,'? r=refresh time | m=mode'); } );Die CSS Liste (<style>) ist etwas umfangreicher als es hier benötigt wird - aus einem anderen Projekt genommen und zukünftig vielleicht gut geeignet.
-
Falls du einen Verbraucher ohne WLAN ausschließlich per Bluetooth steuern willst, ist es am einfachsten dafür einen Gen 3/4 Shelly zu verenden. Das kannst du ja mit deinem Plug S Gen 3 einmal testen.
Wenn du ein Plus Gerät (=Gen 2) dafür nehmen willst, solltest du JavaScript können und dich in das Shelly Skripten einarbeiten. Wenn du das willst und Grundlagenkenntnisse hast, helfe ich dir gerne weiter.
-
Gehören beide Abbildungen nicht zu deinem Rollladen Shelly der Generation 3/4?
Edit:
Ich sah soeben, dass diese Abb. zu einem PlugS Gen 3 gehören. Sind diese Optionen bei deinem Rollladen Shelly nicht vorhanden oder gehört Letzterer nicht der Generation 3/4 an?
Wenn du einen PLugS Gen 3 als Gateway nutzt, dann wird dieser mit einem Rollladen Shelly (vermutlich) nur per WLAN kommunizieren können.
-
Szenen arbeiten Cloud basiert. Cloud nicht erreichbar -> Funktion weg. Besser ist es lokal arbeiten zu lassen. Ich habe einen BLU Motion liegen, aber noch nicht ausgepackt. Auf Grund von anderen BLU Geräten und einem Gen 3/4 Shelly habe ich ein kleines Skript erstellt, welches vermutlich auch für deinen Zweck genutzt werden könnte. Das Skript braucht nur auf Grund der Nachricht vom BLU Motion geeignete Nachrichten an das Zielgerät zu senden. Auch der Einsatz eines Timers könnte ggf. nützlich sein.
Das grundlegende kleine Skript ist mit Anleitung hier zu finden.
Bei Muße kann ich den BLU Motion dazu mal testen ...
-
Das bewirkt nicht das Gleiche wie die Amazon Cloud Routinen, da einer Routine ein eigener Sprachbefehl zugeordnet ist.
Bei deiner "Lösung" wird hingegen immer ein weiterer (oder auch mehrere) Rollladen geschlossen, wenn derjenige mit den eingetragenen Actions geschlossen wird. Dies erscheint mir nicht zufriedenstellend.
Weil ein Sprachbefehl immer über die Shelly Cloud arbeitet, sehe ich auch keine Lösung per Skript. Dort lässt sich nur erkennen, ob der Auslöser die Shelly Cloud war.
Lassen sich Shelly Szenen per Amazon Cloud aktivieren? Wenn ja, wäre dein Workaround treffend, wenn nein leider nicht.