Zum Hauptinhalt springenZur Hauptnavigation springen

Empfohlene Produkte

📦 Empfohlene Gadgets

Ausgewählte Empfehlungen für dein Smart Home Projekt

Lädt Produkte... (smartswitch → sensor → automation)

🛰️ Shelly LoRa Add-on + Home Assistant

In meinem aktuellen YouTube-Video zeige ich, wie du mit dem Shelly LoRa Add-on Geräte über mehrere Kilometer hinweg steuerst – ganz ohne WLAN.

🎥

YouTube-Video

Lädt Inhalte von YouTube. Durch Klick stimmen Sie dem Laden externer Inhalte zu.

REST-Template & komplette Skripte für LoRa-Wechselschaltung

Hier findest du das passende REST-Template für Home Assistant sowie die Sender- und Empfänger-Skripte mit AES-Verschlüsselung.

Wichtig: Diese Skripte sind speziell für eine LoRa-basierte Wechselschaltung gedacht – du kannst damit den Zustand eines Schalters an ein anderes Shelly-Gerät übertragen, das dann synchron ein- oder ausschaltet.


🔧 Home Assistant REST-Command Template fürs Shelly LoRa Add-on

1 2 3 4 5 6 7 8 9 10 11 rest_command: shelly_send_lora: url: "http://192.168.178.137/rpc/Script.Eval" method: POST headers: Content-Type: application/json payload: > { "id": 3, "code": "sendMessage(\"{{ nachricht }}\")" }

➡️ Passe die IP-Adresse (192.168.178.137) und die id an dein auszuführendes Script an. Damit kannst du über eine Automation z. B. "100" (AN) oder "000" (AUS) an ein Shelly-Gerät senden, das die Nachricht über LoRa verschlüsselt weiterleitet.

📤 Sender-Skript – Shelly Gen4 (z. B. Shelly 1 Gen4)

Dieses Skript läuft auf dem Gerät, das den Zustand (z. B. per Taster oder Automation) erkennt und per LoRa weitergibt. Es verschlüsselt die Nachricht mit AES-256, fügt eine Prüfsumme hinzu und schickt sie zum Empfänger.

🧠 Einsatz: z. B. im Haus – die Lampe im Garten wird geschaltet.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 // 🔐 --- Konfiguration & AES-Verschlüsselung --- const aesKey = 'dd469421e5f4089a1418ea24ba37c61bdd469421e5f4089a1418ea24ba37c61b'; const CHECKSUM_SIZE = 4; let lastRemoteState = null; // Letzter per LoRa empfangener Zustand // 📦 Nachricht verschlüsseln function encryptMessage(msg, keyHex) { function fromHex(hex) { const arr = new ArrayBuffer(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { arr[i / 2] = parseInt(hex.substr(i, 2), 16); } return arr; } function padRight(msg, blockSize) { const paddingSize = (blockSize - msg.length % blockSize) % blockSize; for (let i = 0; i < paddingSize; i++) msg += ' '; return msg; } msg = msg.trim(); const formattedMsg = padRight(msg, 16); const key = fromHex(keyHex); return AES.encrypt(formattedMsg, key, { mode: 'ECB' }); } // ✔️ Prüfsumme erzeugen function generateChecksum(msg) { let checksum = 0; for (let i = 0; i < msg.length; i++) { checksum ^= msg.charCodeAt(i); } let hexChecksum = checksum.toString(16); while (hexChecksum.length < CHECKSUM_SIZE) { hexChecksum = '0' + hexChecksum; } return hexChecksum.slice(-CHECKSUM_SIZE); } // 📡 Nachricht senden + optional Relais lokal schalten function sendMessage(message) { const checkSumMessage = generateChecksum(message) + message; const encryptedMessage = encryptMessage(checkSumMessage, aesKey); Shelly.call( 'Lora.SendBytes', { id: 100, // 🛰 Zielgerät-ID data: btoa(encryptedMessage) }, function (_, err_code, err_msg) { if (err_code !== 0) { print('LoRa send error:', err_code, err_msg); } else { print('LoRa message sent:', message); } } ); // 🟢 Lokales Relais auf Wunsch mitsteuern if (message === "100") { Shelly.call("Switch.Set", { id: 0, on: true }); print("Lokales Relais: EIN"); } else if (message === "000") { Shelly.call("Switch.Set", { id: 0, on: false }); print("Lokales Relais: AUS"); } } // 🔁 Lokalen Schaltzustand überwachen und senden Shelly.addStatusHandler(function (e) { if (e.component === "switch:0" && e.delta && typeof e.delta.output === "boolean") { const isOn = e.delta.output; if (lastRemoteState !== null) { print("[LoRa] Änderung durch entfernte Nachricht – kein Re-Senden"); lastRemoteState = null; return; } const message = isOn ? "100" : "000"; sendMessage(message); } }); // 📥 LoRa-Nachrichten entschlüsseln function decryptMessage(buffer, keyHex) { function fromHex(hex) { const arr = new ArrayBuffer(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { arr[i / 2] = parseInt(hex.substr(i, 2), 16); } return arr; } function hex2a(hex) { let str = ''; for (let i = 0; i < hex.length; i += 2) { str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); } return str; } function toHex(buffer) { let s = ''; for (let i = 0; i < buffer.length; i++) { s += (256 + buffer[i]).toString(16).substr(-2); } return s; } const key = fromHex(keyHex); const decrypted = AES.decrypt(buffer, key, { mode: 'ECB' }); if (!decrypted || decrypted.byteLength === 0) return; const hex = toHex(decrypted); return hex2a(hex).trim(); } // 🛡 Prüfsumme prüfen function verifyMessage(message) { if (!message || message.length < CHECKSUM_SIZE + 1) return null; const receivedChecksum = message.slice(0, CHECKSUM_SIZE); const payload = message.slice(CHECKSUM_SIZE); const expectedChecksum = generateChecksum(payload); return (receivedChecksum === expectedChecksum) ? payload : null; } // 🎯 Event-Handler für LoRa-Empfang Shelly.addEventHandler(function (event) { if (!event || event.name !== 'lora' || !event.info || !event.info.data) return; const encryptedMsg = atob(event.info.data); const decrypted = decryptMessage(encryptedMsg, aesKey); if (!decrypted) return; const clean = verifyMessage(decrypted); if (!clean) { print("[LoRa] Checksum mismatch"); return; } print("[LoRa] Received:", clean); if (clean === "100") { lastRemoteState = true; Shelly.call("Switch.Set", { id: 0, on: true }); } else if (clean === "000") { lastRemoteState = false; Shelly.call("Switch.Set", { id: 0, on: false }); } else { print("[LoRa] Unknown command:", clean); } });

📥 Empfänger-Skript – Shelly Gen4 (z. B. im Gartenhaus)

Dieses Skript läuft auf dem Gerät, das per LoRa eine Nachricht empfängt und das eigene Relais entsprechend schaltet. Gleichzeitig wird der Status bei lokaler Änderung wieder zurückgemeldet.

🧠 Einsatz: z. B. Gartenhaus, Carport oder Keller – das Relais spiegelt den Zustand des entfernten Schalters.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 // 🔐 --- GRUNDKONFIGURATION --- const aesKey = 'dd469421e5f4089a1418ea24ba37c61bdd469421e5f4089a1418ea24ba37c61b'; const CHECKSUM_SIZE = 4; let lastLoRaCommand = null; // Schützt vor Echo bei Status-Rückmeldung // ✔️ --- CHECKSUMME ERZEUGEN --- function generateChecksum(msg) { let checksum = 0; for (let i = 0; i < msg.length; i++) { checksum ^= msg.charCodeAt(i); } let hexChecksum = checksum.toString(16); while (hexChecksum.length < CHECKSUM_SIZE) { hexChecksum = '0' + hexChecksum; } return hexChecksum.slice(-CHECKSUM_SIZE); } // 🛡 --- CHECKSUMME VERIFIZIEREN --- function verifyMessage(message) { if (message.length < CHECKSUM_SIZE + 1) { print('[LoRa] invalid message (too short)'); return; } const receivedCheckSum = message.slice(0, CHECKSUM_SIZE); const _message = message.slice(CHECKSUM_SIZE); const expectedChecksum = generateChecksum(_message); if (receivedCheckSum !== expectedChecksum) { print('[LoRa] invalid message (checksum mismatch)'); return; } return _message; } // 🔓 --- NACHRICHT ENTSCHLÜSSELN (AES) --- function decryptMessage(buffer, keyHex) { function fromHex(hex) { const arr = new ArrayBuffer(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { arr[i / 2] = parseInt(hex.substr(i, 2), 16); } return arr; } function hex2a(hex) { let str = ''; for (let i = 0; i < hex.length; i += 2) { str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); } return str; } function toHex(buffer) { let s = ''; for (let i = 0; i < buffer.length; i++) { s += (256 + buffer[i]).toString(16).substr(-2); } return s; } const key = fromHex(keyHex); const decrypted = AES.decrypt(buffer, key, { mode: 'ECB' }); if (!decrypted || decrypted.byteLength === 0) { print('[LoRa] invalid msg (empty decryption result)'); return; } const hex = toHex(decrypted); const checksumMessage = hex2a(hex).trim(); return verifyMessage(checksumMessage); } // 📥 --- LoRa-NACHRICHT EMPFANGEN UND RELAIS SCHALTEN --- Shelly.addEventHandler(function (event) { if (!event || event.name !== 'lora' || !event.info || !event.info.data) return; const encryptedMsg = atob(event.info.data); const decryptedMessage = decryptMessage(encryptedMsg, aesKey); if (!decryptedMessage) return; print("[LoRa] Message received:", decryptedMessage); const cmd = decryptedMessage.trim(); if (cmd === "100") { lastLoRaCommand = true; Shelly.call("Switch.Set", { id: 0, on: true }); } else if (cmd === "000") { lastLoRaCommand = false; Shelly.call("Switch.Set", { id: 0, on: false }); } else { print("[LoRa] Unknown command:", cmd); } }); // 🔐 --- NACHRICHT ENTSCHLÜSSELN (ZUM ZURÜCKSENDEN) function encryptMessage(msg, keyHex) { function fromHex(hex) { const arr = new ArrayBuffer(hex.length / 2); for (let i = 0; i < hex.length; i += 2) { arr[i / 2] = parseInt(hex.substr(i, 2), 16); } return arr; } function padRight(msg, blockSize) { const paddingSize = (blockSize - msg.length % blockSize) % blockSize; for (let i = 0; i < paddingSize; i++) { msg += ' '; } return msg; } const key = fromHex(keyHex); const formattedMsg = padRight(msg, 16); return AES.encrypt(formattedMsg, key, { mode: 'ECB' }); } // 🔁 --- STATUS ZURÜCKSENDEN (Z.B. AN DEN SENDER) function sendLoRaStateBack(isOn) { const msg = isOn ? "100" : "000"; const fullMsg = generateChecksum(msg) + msg; const encrypted = encryptMessage(fullMsg, aesKey); Shelly.call("Lora.SendBytes", { id: 101, // 🛰 Zieladresse (z. B. ursprünglicher Sender) data: btoa(encrypted) }, function (_, code, err) { if (code !== 0) { print("[LoRa] Send back failed:", err); } else { print("[LoRa] State sent back:", msg); } }); } // ⚡ --- LOKALE RELAISÄNDERUNG ERKENNEN UND ANTWORT SENDEN Shelly.addStatusHandler(function (e) { if (e.component === "switch:0" && e.delta && typeof e.delta.output === "boolean") { if (lastLoRaCommand !== null && e.delta.output === lastLoRaCommand) { print("[LoRa] Ignoring echo from LoRa-triggered action."); lastLoRaCommand = null; return; } sendLoRaStateBack(e.delta.output); } });

📌 Fazit zum Shelly LoRa Add-on

✅ Mit diesen beiden Skripten kannst du eine vollständige, sichere Wechselschaltung über LoRa aufbauen – unabhängig von WLAN oder Cloud. Perfekt für Schuppen, Gartenhäuser, Carports, Keller – überall dort, wo ein Kabel nicht hinreicht.

📽️ Video mit Live-Demo & Erklärung des Shelly LoRa Add-on:


💬 Fragen, Ideen oder eigene Umsetzung? Hinterlass gerne einen Kommentar unter dem Video!


Shopping-Empfehlungen

Passende smartswitch-Produkte

Weitere empfohlene Produkte für dein Smart Home

Lädt Produkte... (smartswitch → sensor → automation)

Artikel teilen

Teilen: