NEWS
Shelly 3PM Pro Emulator
-
Hallo,
ich habe mir einen Marstek Venus Stromspeicher zugelegt und dieser kommuniziert unter anderem mit dem Shelly 3PM Pro zum Steuern des Lade- und Entladevorgangs.
Da ich bereits einen Stromzähler im Zählerschrank verbaut habe und dieser über den Modbus-Adapter die aktuellen Stromwerte liefert, wäre es aus meiner Sicht unnötig einen zweiten Stromzähler einzubauen.Ich habe auch schon die Projekte uni-meter und B2500 meter in Github gefunden, wovon letzteres sogar als Homeassistant-Addon verfügbar ist, aber ist gibt keine native IOBroker Version.
Frage deshalb: Gibt es einen Weg wie man Werte vom Modbus Adapter / eines IOBroker angelegten Objekt in ein virtuelles W-lan Signal verwandelt, dass einen Shelly 3PM Pro emuliert?
Grüße
miwi
-
Ich hänge mich hier mal mit dran, da ich die gleiche Frage bzw. ein ähnliches Problem habe.
-
Ich schätze dass das was für euch sein könnte:
https://github.com/sdeigm/uni-meter
OK - grad erst gesehen, dass unimeter eh schon genannt wurde.
-
Das GitHub B2500 Projekt hat eine Möglichkeit IOB Datenpunkte anzubinden. In diese Datenpunkte schreibst du deine Werte vom Zähler.
-
@mikerow sagte in Shelly 3PM Pro Emulator:
Das GitHub B2500 Projekt hat eine Möglichkeit IOB Datenpunkte anzubinden. In diese Datenpunkte schreibst du deine Werte vom Zähler.
Puh, weiß jemand was aktuell das einfachste ist den akku zu steuern? so wie ich das gelesen habe entweder mit ne shelly, oder shelly emulieren, oder Modbus und selbst steuern.
Hat jemand da schon was im Einsatz und kann berichten, was am besten wäre?Gruß und Besten Dank.
-
@ple Das absolut einfachste ist natürlich der Marstek Zähler CT001 .. 003. Dann der Shelly.
@miwi: was spricht denn gegen Uni-Meter? Das ist doch alles virtuell. Du musst nur einen Container in Docker installieren.
Oder ist dein aktueller, per Mod-Bus ausgelesener Zähler, nicht kompatibel mit Uni-Meter? Wobei uni-Meter ja auch iob dp auslesen kann. Ist doch absolut genau das, was du suchst. -
Interessante Lösung dieses Uni-Meter. Habe es mal parallel zum ioBroker im Docker erstellt und versucht das Uni-Meter für input Tasmota (habe ein BitShake IR SmartReader) zu konfigurieren.
Startet schon mal ohne Fehlermeldung. Allerdings bin ich mir nicht sicher, ob die Tasmotasettings auch Werte liefern.
So ein IR SmartReader ist auf jeden Fall billiger als ein Shelly, den ggf. notwendigen Elektriker für den Einbau kann man sich sparen und Spaß macht so eine Bastelei auch
-
Kämpfe zwar noch damit den docker container vernünftig einzurichten. Aber Werte liefert der Shelly Emulator
{"id":0,"a_current":0.54,"a_voltage":230,"a_act_power":124.67,"a_aprt_power":124.67,"a_pf":1,"a_freq":50,"b_current":0.54,"b_voltage":230,"b_act_power":124.67,"b_aprt_power":124.67,"b_pf":1,"b_freq":50,"c_current":0.54,"c_voltage":230,"c_act_power":124.67,"c_aprt_power":124.67,"c_pf":1,"c_freq":50,"total_current":1.62,"total_act_power":374.01,"total_aprt_power":374.01}
Nutzt hier irgendwer Uni-Meter????
Mod-Edit
Bitte Codetags </> benutzen! -
ich habe zwar den uni-meter im docker am laufen, aber die Marstek APP 1.6.43 findet diesen nicht.
uni-meter { output = "uni-meter.output-devices.shelly-pro3em" input = "uni-meter.input-devices.shelly-3em" http-server { port:4711 } output-devices { shelly-pro3em { mac = "DC4F22764880" hostname = "shellypro3em-DC4F22764880" port = 4711 udp-port = 1010 udp-interface ="0.0.0.0" min-sample-period = 5000ms } } input-devices { shelly-3em { url = "http://10.xx.0.xxx" } } }
-
Komme auch nicht wirklich weiter, habe ein JS-Skript für den IOBroker mit Hilfe von einer KI gebaut,
Der Marstek Venus E kann sich mit dem Simulierten Shelly 3emPro verbinden, dort werden auch Realistische Werte angezeigt, wenn über 100Watt.
Hatte vorher alle drei Phasen ausprobiert, habe ich allerdings nicht hinbekommen.Bin wieder bei meinem Problem, was ich auch bei anderen Versuchen hatte:
Wenn der Marstek sieht, er soll entladen, macht er das nicht zum reelen Wert, also z.B. 500 Watt, sondern fährt auf die Begrenzung in der App (800 Watt).
Laut Anzeige 797 Watt, und speist so 300 Watt in das Netz ein, was ich natürlich nicht so erwünsche.
(5 Tage investiert und das Ding macht immer noch was es will)
Hat dort jemand eine Lösung dafür?
(Firmware v152)Mein JS-Script für Iobroker:
// =================================================== // Shelly EM3 Pro Emulation für Marstek Venus e (ANTI-797W OPTIMIERT) // by Gismoh // =================================================== // FIXES: Positive Werte + Kurze Intervalle + 3-Phasen + Stabilisierung // MARSTEK ANTI-797W KONFIGURATION var MARSTEK_CONFIG = { // === COMMUNITY-BEWÄHRTE FIXES === forcePositiveValues: true, // ✅ FIX: Nur positive Werte (Community-Lösung) useThreePhases: true, // ✅ FIX: Alle 3 Phasen nutzen für Stabilität fastPolling: true, // ✅ FIX: Kurze Intervalle stabilizeSmallValues: true, // ✅ FIX: Kleine Schwankungen ignorieren // === DEBUGGING === debugMode: true, // ✅ AKTIVIERT für App-HTTP-Debugging logInterval: 10000, // Häufigeres Logging für bessere Überwachung // === LEGACY (deaktiviert) === useAbsoluteValues: false, invertSignForTest: false, clampNegativeToZero: false, separateInputOutput: false }; // OPTIMIERTE KONFIGURATION var SHELLY_EM3_INSTANCE = 'shelly.0.SHEM-3#XXXXXXXXXXXXXX#1'; // DEINE Shelly EM3 Instanz ID var EMULATION_PORT = 1010; var UPDATE_INTERVAL = MARSTEK_CONFIG.fastPolling ? 2000 : 5000; // ✅ 2s statt 5s // Module laden var http = require('http'); var url = require('url'); var dgram = require('dgram'); var os = require('os'); // Globale Variablen var currentPowerTotal = 0; var currentPowerL1 = 0; var currentPowerL2 = 0; var currentPowerL3 = 0; var energyTotal = 0; var energyL1 = 0; var energyL2 = 0; var energyL3 = 0; // ✅ ECHTE SPANNUNGSWERTE vom Shelly var voltageL1 = 230.0; var voltageL2 = 230.0; var voltageL3 = 230.0; // Performance & Debugging var lastLogTime = 0; var requestCounter = 0; var problemCounter = 0; // Zähler für 797W Probleme // Server-Instanzen var serverInstance = null; var udpSocket = null; // ✅ NEUE FUNKTION: Anti-797W Power Calculation mit Venus E Hardware-Limits function calculateAnti797WPower(originalPower) { var result = { originalPower: originalPower, stabilizedPower: originalPower, phase1Power: 0, phase2Power: 0, phase3Power: 0, isStabilized: false, explanation: '', confidence: 'high' }; // ✅ FIX 1: Venus E Hardware-Limit beachten (100W Minimum) if (Math.abs(originalPower) < 100) { result.stabilizedPower = 0; result.isStabilized = true; result.explanation = 'Unter 100W Venus E Minimum → 0W'; result.confidence = 'hardware-limit'; } // ✅ FIX 2: Kleine Werte zusätzlich stabilisieren (50W Puffer über Hardware-Limit) else if (MARSTEK_CONFIG.stabilizeSmallValues && Math.abs(originalPower) < 150) { result.stabilizedPower = 0; result.isStabilized = true; result.explanation = 'Unter 150W → Venus E Hysterese-Schutz'; result.confidence = 'hysteresis'; } // ✅ FIX 3: Positive Werte forcieren (Community-bewährte Lösung) if (MARSTEK_CONFIG.forcePositiveValues && result.stabilizedPower !== 0) { result.stabilizedPower = Math.abs(result.stabilizedPower); result.explanation += result.explanation ? ' + ' : ''; result.explanation += 'Positive Werte forciert (Community-Fix)'; } // ✅ FIX 4: Drei-Phasen-Verteilung für Stabilität if (MARSTEK_CONFIG.useThreePhases) { var powerPerPhase = result.stabilizedPower / 3; result.phase1Power = powerPerPhase; result.phase2Power = powerPerPhase; result.phase3Power = powerPerPhase; result.explanation += result.explanation ? ' + ' : ''; result.explanation += '3-Phasen-Verteilung'; } else { // Legacy: Nur Phase 1 result.phase1Power = result.stabilizedPower; result.phase2Power = 0; result.phase3Power = 0; } // Sicherheitsbegrenzung if (Math.abs(result.stabilizedPower) > 2000) { result.stabilizedPower = result.stabilizedPower > 0 ? 2000 : -2000; result.explanation += ' + Sicherheitsbegrenzung 2000W'; result.confidence = 'limited'; } if (MARSTEK_CONFIG.debugMode) { console.log('🔧 ANTI-797W: ' + originalPower + 'W → ' + result.stabilizedPower + 'W (' + result.explanation + ')'); if (Math.abs(originalPower) > 750 && Math.abs(originalPower) < 850) { problemCounter++; console.log('⚠️ 797W-BEREICH ERKANNT! Problem #' + problemCounter + ' - Wert: ' + originalPower + 'W'); } // ✅ NEUE WARNUNG: Interessanter Bereich zwischen 100-200W (wo Venus E aktiviert) if (Math.abs(originalPower) >= 100 && Math.abs(originalPower) <= 200 && result.stabilizedPower > 0) { console.log('🎯 VENUS E AKTIVIERUNGSBEREICH: ' + originalPower + 'W → Venus bekommt ' + result.stabilizedPower + 'W'); } } return result; } // Tibber Pulse + Shelly Backup Funktion (optimiert) function readShellyValues() { try { // HAUPTQUELLE: Tibber Pulse var tibberPower = getState('tibberlink.0.LocalPulse.0.Power').val; // BACKUP: Shelly EM3 Pro - Leistungswerte var shellyL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val || 0; var shellyL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Power').val || 0; var shellyL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Power').val || 0; var shellyTotal = shellyL1 + shellyL2 + shellyL3; // ✅ ECHTE SPANNUNGSWERTE vom Shelly auslesen voltageL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Voltage').val || 230.0; voltageL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Voltage').val || 230.0; voltageL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Voltage').val || 230.0; // ✅ VERBESSERTE PLAUSIBILITÄTSPRÜFUNG var tibberValid = (tibberPower !== null && tibberPower !== undefined && Math.abs(tibberPower) < 15000 && !isNaN(tibberPower)); if (tibberValid) { currentPowerTotal = tibberPower; if (MARSTEK_CONFIG.debugMode && Date.now() - lastLogTime > MARSTEK_CONFIG.logInterval) { console.log('📊 Tibber Pulse: ' + tibberPower + 'W (Primärquelle)'); } } else { currentPowerTotal = shellyTotal; console.log('🔄 FALLBACK zu Shelly: ' + shellyTotal + 'W (Tibber invalid: ' + tibberPower + ')'); } // Legacy-Kompatibilität currentPowerL1 = shellyL1; currentPowerL2 = shellyL2; currentPowerL3 = shellyL3; // Energie-Werte energyL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total').val || 0; energyL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Total').val || 0; energyL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Total').val || 0; energyTotal = energyL1 + energyL2 + energyL3; if (MARSTEK_CONFIG.debugMode && Date.now() - lastLogTime > (MARSTEK_CONFIG.logInterval * 2)) { console.log('🔌 Spannungen: L1=' + voltageL1.toFixed(1) + 'V, L2=' + voltageL2.toFixed(1) + 'V, L3=' + voltageL3.toFixed(1) + 'V'); } } catch (error) { console.error('❌ Fehler beim Lesen der Messwerte: ' + error); // Fallback auf letzte gültige Werte } } // Lokale IP-Adresse ermitteln function getLocalIP() { try { var interfaces = os.networkInterfaces(); for (var interfaceName in interfaces) { var addresses = interfaces[interfaceName]; for (var i = 0; i < addresses.length; i++) { var address = addresses[i]; if (address.family === 'IPv4' && !address.internal) { return address.address; } } } } catch (e) { console.log('⚠️ Fehler beim Ermitteln der IP-Adresse'); } return '192.168.1.100'; // Anonymisierte Standard-IP } // ✅ OPTIMIERTE Shelly 3EM Pro Response (Anti-797W) mit getrennten Anzeige- und Steuerwerten function createOptimizedShelly3EMResponse() { // Anti-797W Calculation für STEUERUNG var powerCalc = calculateAnti797WPower(currentPowerTotal); // ✅ GETRENNTE WERTE: var displayPower = currentPowerTotal; // FÜR APP: Echte Werte anzeigen var controlPower = powerCalc.stabilizedPower; // FÜR VENUS E: Bearbeitete Steuerung if (MARSTEK_CONFIG.debugMode && requestCounter % 10 === 0) { console.log('🚀 GETRENNT: Anzeige=' + displayPower + 'W, Steuerung=' + controlPower + 'W'); console.log(' Phase1: ' + powerCalc.phase1Power.toFixed(1) + 'W, Phase2: ' + powerCalc.phase2Power.toFixed(1) + 'W, Phase3: ' + powerCalc.phase3Power.toFixed(1) + 'W'); } return { "id": 0, "src": "shellypro3em-anti797w", "result": { "switch:0": { "id": 0, "output": false }, // ✅ PHASE 1 - ANZEIGE: Echte Werte, STEUERUNG: Bearbeitet "em:0": { "id": 0, "a_current": voltageL1 > 0 ? Math.abs(displayPower / voltageL1) : 0, // App: Echter Strom "a_voltage": voltageL1, "a_power": displayPower, // App: Echter Wert "a_act_power": powerCalc.phase1Power, // Venus E: Bearbeitet "a_pf": displayPower >= 0 ? 1.0 : -1.0, "a_freq": 50.0, "a_total_act_energy": energyL1, "a_total_act_ret_energy": 0 }, // ✅ PHASE 2 - Getrennte Behandlung "em:1": { "id": 1, "a_current": voltageL2 > 0 ? Math.abs(currentPowerL2 / voltageL2) : 0, "a_voltage": voltageL2, "a_power": currentPowerL2, // App: Echter L2-Wert "a_act_power": MARSTEK_CONFIG.useThreePhases ? powerCalc.phase2Power : 0, // Venus E: Bearbeitet "a_pf": currentPowerL2 >= 0 ? 1.0 : -1.0, "a_freq": 50.0, "a_total_act_energy": MARSTEK_CONFIG.useThreePhases ? energyL2 : 0, "a_total_act_ret_energy": 0 }, // ✅ PHASE 3 - Getrennte Behandlung "em:2": { "id": 2, "a_current": voltageL3 > 0 ? Math.abs(currentPowerL3 / voltageL3) : 0, "a_voltage": voltageL3, "a_power": currentPowerL3, // App: Echter L3-Wert "a_act_power": MARSTEK_CONFIG.useThreePhases ? powerCalc.phase3Power : 0, // Venus E: Bearbeitet "a_pf": currentPowerL3 >= 0 ? 1.0 : -1.0, "a_freq": 50.0, "a_total_act_energy": MARSTEK_CONFIG.useThreePhases ? energyL3 : 0, "a_total_act_ret_energy": 0 }, // ✅ GESAMT-WERTE - Getrennte Anzeige und Steuerung "em1:0": { "id": 0, "total_current": Math.abs(displayPower / ((voltageL1 + voltageL2 + voltageL3) / 3)), "total_act_power": displayPower, // App: Echter Gesamt-Wert "total_power": controlPower, // Venus E: Bearbeiteter Steuer-Wert "total_aprt_power": Math.abs(displayPower), "total_act_energy": energyTotal, "total_act_ret_energy": 0 }, "sys": { "mac": "XX:XX:XX:XX:XX:XX", // Anonymisierte MAC "restart_required": false, "time": new Date().toISOString(), "unixtime": Math.floor(Date.now() / 1000), "uptime": Math.floor(Date.now() / 1000), "ram_size": 262144, "ram_free": 160000, "fs_size": 524288, "fs_free": 200000, "cfg_rev": 1, "kvs_rev": 1, "schedule_rev": 1, "webhook_rev": 1, "available_updates": {} }, "wifi": { "sta_ip": getLocalIP(), "status": "got ip", "ssid": "DEIN-WIFI-NAME", // Anonymisierter WLAN-Name "rssi": -45 }, "mqtt": { "connected": false }, "cloud": { "connected": false } } }; } // HTTP Server (optimiert) var server = http.createServer(function(req, res) { var parsedUrl = url.parse(req.url, true); // CORS Headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // Request Counter für Debugging requestCounter++; if (MARSTEK_CONFIG.debugMode && requestCounter % 20 === 0) { console.log('🌐 HTTP Request #' + requestCounter + ': ' + req.method + ' ' + req.url); } // ✅ HTTP-ENDPUNKTE: Maximales Logging für App-Debugging if (parsedUrl.pathname === '/rpc/Shelly.GetStatus' || parsedUrl.pathname === '/status' || parsedUrl.pathname === '/rpc') { console.log('📱 ===== APP-HTTP RESPONSE ====='); console.log('🎯 Endpoint: ' + parsedUrl.pathname); console.log('💡 Sende echte Werte: ' + currentPowerTotal + 'W'); console.log('⚡ Phase 1: ' + currentPowerTotal + 'W'); console.log('⚡ Phase 2: 0W (Original-Struktur)'); console.log('⚡ Phase 3: 0W (Original-Struktur)'); console.log('====================================='); res.writeHead(200, { 'Content-Type': 'application/json' }); var response = createSimpleAppResponse(); res.end(JSON.stringify(response)); return; } // Shelly Info API if (parsedUrl.pathname === '/rpc/Shelly.GetInfo' || parsedUrl.pathname === '/settings') { console.log('📱 ===== APP-INFO ANFRAGE ====='); console.log('🎯 Endpoint: ' + parsedUrl.pathname); console.log('====================================='); res.writeHead(200, { 'Content-Type': 'application/json' }); var infoResponse = { "id": 0, "src": "shellypro3em-anti797w", "result": { "name": "Shelly Pro 3EM Anti-797W (Marstek Optimiert)", "id": "shellypro3em-anti797w", "mac": "XX:XX:XX:XX:XX:XX", // Anonymisierte MAC "slot": 1, "model": "SPRO-3EM", "gen": 2, "fw_id": "20230913-114414/v1.14.0-anti797w", "ver": "1.14.0-optimized", "app": "Pro3EM-VenusOptimized", "auth_en": false, "auth_domain": null } }; res.end(JSON.stringify(infoResponse)); return; } // Legacy-Endpunkte if (parsedUrl.pathname === '/shelly') { console.log('📱 ===== APP-LEGACY ANFRAGE ====='); console.log('🎯 Endpoint: /shelly (Device Discovery)'); console.log('====================================='); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ "type": "SPRO-3EM-OPTIMIZED", "mac": "XX:XX:XX:XX:XX:XX", // Anonymisierte MAC "auth": false, "fw": "1.14.0-anti797w", "discoverable": true, "longid": 1, "num_outputs": 1, "num_meters": 3 })); return; } // Device Description if (parsedUrl.pathname === '/description.xml') { res.writeHead(200, { 'Content-Type': 'text/xml' }); var xml = '<?xml version="1.0"?>\n' + '<root xmlns="urn:schemas-upnp-org:device-1-0">\n' + '<device>\n' + '<deviceType>urn:shelly:device:pro3em:1</deviceType>\n' + '<friendlyName>Shelly Pro 3EM Anti-797W (Marstek Optimiert)</friendlyName>\n' + '<manufacturer>Allterco-Optimized</manufacturer>\n' + '<modelName>Shelly Pro 3EM Anti-797W</modelName>\n' + '<UDN>uuid:shelly-pro3em-anti797w</UDN>\n' + '</device>\n' + '</root>'; res.end(xml); return; } // 404 für unbekannte Anfragen res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found: ' + parsedUrl.pathname); }); // ✅ VEREINFACHTE RPC Request Handler - ALLE bekommen echte Werte (wie altes Script) function handleOptimizedRPCRequest(msg, rinfo, socket) { var message = msg.toString(); try { if (message.includes('EM.GetStatus') || message.includes('Shelly.GetStatus')) { var powerCalc = calculateAnti797WPower(currentPowerTotal); requestCounter++; var now = Date.now(); // ✅ EINHEITLICHE BEHANDLUNG: Alle Anfragen gleich (wie altes Script) console.log('🎯 Marstek Anfrage #' + requestCounter + ' von ' + rinfo.address + ' - Sende: ' + powerCalc.stabilizedPower + 'W'); // ✅ Reduziertes Logging if (now - lastLogTime > MARSTEK_CONFIG.logInterval || requestCounter % 25 === 0) { console.log(' 📊 Daten: ' + currentPowerTotal + 'W → ' + powerCalc.stabilizedPower + 'W'); console.log(' 🔧 3-Phasen: ' + MARSTEK_CONFIG.useThreePhases + ', Positiv: ' + MARSTEK_CONFIG.forcePositiveValues); console.log(' ⚡ Phase1: ' + powerCalc.phase1Power.toFixed(1) + 'W, Phase2: ' + powerCalc.phase2Power.toFixed(1) + 'W, Phase3: ' + powerCalc.phase3Power.toFixed(1) + 'W'); lastLogTime = now; } // Parse Request ID var requestData; try { requestData = JSON.parse(message); } catch (e) { requestData = { id: 1, params: { id: 0 } }; } // ✅ EINHEITLICHE RESPONSE: Alle bekommen Anti-797W optimierte Werte var rpcResponse = { "id": requestData.id || 1, "src": "shellypro3em-anti797w-" + Math.random().toString(36).substr(2, 6), "dst": "user_1", "result": { "em:0": { "id": 0, "a_current": voltageL1 > 0 ? Math.abs(powerCalc.stabilizedPower / voltageL1) : 0, "a_voltage": voltageL1, "a_act_power": powerCalc.stabilizedPower, // ✅ ANTI-797W Werte für ALLE "a_aprt_power": Math.abs(powerCalc.stabilizedPower), "a_pf": powerCalc.stabilizedPower >= 0 ? 1.0 : -1.0, "a_freq": 50.0, "a_total_act_energy": energyL1, "a_total_act_ret_energy": 0 }, "em:1": { "id": 1, "a_current": MARSTEK_CONFIG.useThreePhases && voltageL2 > 0 ? Math.abs(powerCalc.phase2Power / voltageL2) : 0, "a_voltage": voltageL2, "a_act_power": MARSTEK_CONFIG.useThreePhases ? powerCalc.phase2Power : 0, "a_aprt_power": MARSTEK_CONFIG.useThreePhases ? Math.abs(powerCalc.phase2Power) : 0, "a_pf": 1.0, "a_freq": 50.0, "a_total_act_energy": MARSTEK_CONFIG.useThreePhases ? energyL2 : 0, "a_total_act_ret_energy": 0 }, "em:2": { "id": 2, "a_current": MARSTEK_CONFIG.useThreePhases && voltageL3 > 0 ? Math.abs(powerCalc.phase3Power / voltageL3) : 0, "a_voltage": voltageL3, "a_act_power": MARSTEK_CONFIG.useThreePhases ? powerCalc.phase3Power : 0, "a_aprt_power": MARSTEK_CONFIG.useThreePhases ? Math.abs(powerCalc.phase3Power) : 0, "a_pf": 1.0, "a_freq": 50.0, "a_total_act_energy": MARSTEK_CONFIG.useThreePhases ? energyL3 : 0, "a_total_act_ret_energy": 0 }, "em1:0": { "id": 0, "total_current": Math.abs(powerCalc.stabilizedPower / ((voltageL1 + voltageL2 + voltageL3) / 3)), "total_act_power": powerCalc.stabilizedPower, // ✅ ANTI-797W Wert für ALLE "total_aprt_power": Math.abs(powerCalc.stabilizedPower), "total_act_energy": energyTotal, "total_ret_energy": 0, "user_calibrated_phase": [true, true, true] } } }; var responseStr = JSON.stringify(rpcResponse); socket.send(responseStr, rinfo.port, rinfo.address, function(err) { if (err && MARSTEK_CONFIG.debugMode) { console.log('❌ UDP Sendefehler: ' + err); } }); } // Fallback für andere JSON RPC Anfragen else { try { var rpcRequest = JSON.parse(message); if (rpcRequest.method) { var fallbackResponse = { "id": rpcRequest.id || 1, "src": "shellypro3em-anti797w", "result": createOptimizedShelly3EMResponse().result }; var responseStr = JSON.stringify(fallbackResponse); socket.send(responseStr, rinfo.port, rinfo.address); } } catch (e) { // Ignore invalid JSON } } } catch (error) { console.log('❌ UDP-Verarbeitungsfehler: ' + error); } } // UDP Socket für Discovery function createUDPSocket() { udpSocket = dgram.createSocket('udp4'); udpSocket.on('message', function(msg, rinfo) { var message = msg.toString(); // Discovery Response if (message.includes('M-SEARCH') || message.includes('shelly') || message.includes('SSDP')) { var localIP = getLocalIP(); var response = 'HTTP/1.1 200 OK\r\n' + 'ST: urn:shelly:device\r\n' + 'USN: uuid:shelly-pro3em-anti797w\r\n' + 'LOCATION: http://' + localIP + ':' + EMULATION_PORT + '/settings\r\n' + 'SERVER: Shelly/1.14.0-anti797w\r\n' + 'CACHE-CONTROL: max-age=1800\r\n' + '\r\n'; udpSocket.send(response, rinfo.port, rinfo.address); } else { handleOptimizedRPCRequest(msg, rinfo, udpSocket); } }); udpSocket.on('error', function(err) { console.log('❌ UDP Discovery Fehler: ' + err); }); } // ✅ OPTIMIERTE Emulation starten function startOptimizedEmulation() { if (serverInstance) { console.log('⚠️ Emulation läuft bereits'); return; } try { // HTTP Server starten serverInstance = server.listen(EMULATION_PORT, function() { console.log('🚀 === MARSTEK VENUS E ANTI-797W EMULATION GESTARTET ==='); console.log('🔧 Port: ' + EMULATION_PORT); console.log('🌐 URL: http://DEINE-IP:' + EMULATION_PORT); console.log('📱 App Test: http://DEINE-IP:' + EMULATION_PORT + '/status'); console.log('⚡ Update-Intervall: ' + UPDATE_INTERVAL + 'ms (OPTIMIERT)'); console.log(''); console.log('🎯 ANTI-797W FIXES AKTIV:'); console.log(' ✅ Positive Werte: ' + MARSTEK_CONFIG.forcePositiveValues); console.log(' ✅ 3-Phasen-Nutzung: ' + MARSTEK_CONFIG.useThreePhases); console.log(' ✅ Schnelles Polling: ' + MARSTEK_CONFIG.fastPolling + ' (' + UPDATE_INTERVAL + 'ms)'); console.log(' ✅ Wert-Stabilisierung: ' + MARSTEK_CONFIG.stabilizeSmallValues); console.log(' 🔍 Debug-Modus: ' + MARSTEK_CONFIG.debugMode); console.log('=================================================='); }); // UDP Discovery (Port 1900) createUDPSocket(); udpSocket.bind(1900, function() { console.log('📡 UDP Discovery läuft auf Port 1900'); }); // UDP RPC Socket (Port 1010) var rpcSocket = dgram.createSocket('udp4'); rpcSocket.bind(1010, function() { console.log('🔌 UDP RPC Socket läuft auf Port 1010 (Venus E Kommunikation)'); }); rpcSocket.on('message', function(msg, rinfo) { handleOptimizedRPCRequest(msg, rinfo, rpcSocket); }); rpcSocket.on('error', function(err) { console.log('❌ UDP RPC Port 1010 Fehler: ' + err); }); // ✅ OPTIMIERTES Update-Intervall setInterval(readShellyValues, UPDATE_INTERVAL); // Initiale Werte laden readShellyValues(); console.log('🎉 ANTI-797W OPTIMIERUNG BEREIT!'); } catch (error) { console.error('❌ Fehler beim Starten der Emulation: ' + error); } } function stopEmulation() { if (serverInstance) { serverInstance.close(function() { console.log('🛑 Shelly EM3 Pro Emulation gestoppt'); serverInstance = null; }); } if (udpSocket) { udpSocket.close(function() { console.log('🛑 UDP Discovery gestoppt'); }); } } // ✅ ERWEITERTE Überwachungsfunktion function monitorAnti797WBehavior() { console.log(''); console.log('📊 === ANTI-797W MONITORING ==='); console.log('🔧 Aktive Fixes:'); console.log(' • Positive Werte: ' + MARSTEK_CONFIG.forcePositiveValues); console.log(' • 3-Phasen-Verteilung: ' + MARSTEK_CONFIG.useThreePhases); console.log(' • Schnelles Polling: ' + UPDATE_INTERVAL + 'ms'); console.log(' • Wert-Stabilisierung: ' + MARSTEK_CONFIG.stabilizeSmallValues); console.log(''); console.log('📈 Statistiken:'); console.log(' • Requests verarbeitet: ' + requestCounter); console.log(' • 797W-Bereich erkannt: ' + problemCounter + ' mal'); console.log(' • Aktueller Wert: ' + currentPowerTotal + 'W'); if (currentPowerTotal !== 0) { var calc = calculateAnti797WPower(currentPowerTotal); console.log(' • Venus bekommt: ' + calc.stabilizedPower + 'W'); console.log(' • Phase 1/2/3: ' + calc.phase1Power.toFixed(1) + '/' + calc.phase2Power.toFixed(1) + '/' + calc.phase3Power.toFixed(1) + ' W'); } // Warnung bei auffälligen Werten if (Math.abs(currentPowerTotal) > 750 && Math.abs(currentPowerTotal) < 850) { console.log('⚠️ ACHTUNG: Wert im kritischen 797W-Bereich!'); } console.log('================================'); console.log(''); } // ✅ ERWEITERTE Konfigurationsfunktionen function switchAnti797WMode(mode) { console.log('🔧 Wechsle zu Anti-797W Modus: ' + mode); switch(mode) { case 'aggressive': MARSTEK_CONFIG.forcePositiveValues = true; MARSTEK_CONFIG.useThreePhases = true; MARSTEK_CONFIG.fastPolling = true; MARSTEK_CONFIG.stabilizeSmallValues = true; UPDATE_INTERVAL = 1500; // Noch schneller console.log('💪 AGGRESSIVER MODUS: Alle Anti-797W Fixes maximal aktiv'); break; case 'community': MARSTEK_CONFIG.forcePositiveValues = true; MARSTEK_CONFIG.useThreePhases = true; MARSTEK_CONFIG.fastPolling = true; MARSTEK_CONFIG.stabilizeSmallValues = true; UPDATE_INTERVAL = 2000; console.log('👥 COMMUNITY-MODUS: Bewährte Fixes aus den Foren'); break; case 'conservative': MARSTEK_CONFIG.forcePositiveValues = true; MARSTEK_CONFIG.useThreePhases = false; MARSTEK_CONFIG.fastPolling = false; MARSTEK_CONFIG.stabilizeSmallValues = true; UPDATE_INTERVAL = 3000; console.log('🛡️ KONSERVATIVER MODUS: Minimale Änderungen'); break; case 'debug': MARSTEK_CONFIG.forcePositiveValues = true; MARSTEK_CONFIG.useThreePhases = true; MARSTEK_CONFIG.fastPolling = true; MARSTEK_CONFIG.stabilizeSmallValues = true; MARSTEK_CONFIG.debugMode = true; MARSTEK_CONFIG.logInterval = 5000; UPDATE_INTERVAL = 2000; console.log('🔍 DEBUG-MODUS: Maximales Logging für Problemanalyse'); break; case 'original': MARSTEK_CONFIG.forcePositiveValues = false; MARSTEK_CONFIG.useThreePhases = false; MARSTEK_CONFIG.fastPolling = false; MARSTEK_CONFIG.stabilizeSmallValues = false; UPDATE_INTERVAL = 5000; console.log('📜 ORIGINAL-MODUS: Wie das ursprüngliche Script'); break; default: console.log('❌ Unbekannter Modus. Verfügbar: aggressive, community, conservative, debug, original'); return; } console.log('✅ Neue Konfiguration aktiv: Update-Intervall ' + UPDATE_INTERVAL + 'ms'); console.log('📊 Monitoring läuft - beobachte die Venus E Reaktion...'); } // ✅ PROBLEM-DIAGNOSTIK Funktion function diagnoseVenusEProblem() { console.log(''); console.log('🔬 === VENUS E PROBLEM-DIAGNOSE ==='); // Aktueller Zustand console.log('📊 AKTUELLER ZUSTAND:'); console.log(' • Tibber Pulse: ' + (getState('tibberlink.0.LocalPulse.0.Power').val || 'N/A') + 'W'); console.log(' • Shelly L1: ' + (getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val || 'N/A') + 'W'); console.log(' • Shelly L2: ' + (getState(SHELLY_EM3_INSTANCE + '.Emeter1.Power').val || 'N/A') + 'W'); console.log(' • Shelly L3: ' + (getState(SHELLY_EM3_INSTANCE + '.Emeter2.Power').val || 'N/A') + 'W'); console.log(' • Spannung L1/L2/L3: ' + voltageL1.toFixed(1) + 'V / ' + voltageL2.toFixed(1) + 'V / ' + voltageL3.toFixed(1) + 'V'); console.log(' • Verarbeiteter Wert: ' + currentPowerTotal + 'W'); // Script-Status console.log(''); console.log('🔧 SCRIPT-STATUS:'); console.log(' • Requests verarbeitet: ' + requestCounter); console.log(' • 797W-Erkennungen: ' + problemCounter); console.log(' • Update-Intervall: ' + UPDATE_INTERVAL + 'ms'); console.log(' • Server läuft: ' + (serverInstance ? 'JA' : 'NEIN')); // Anti-797W Status console.log(''); console.log('🎯 ANTI-797W FIXES:'); console.log(' • Positive Werte forciert: ' + (MARSTEK_CONFIG.forcePositiveValues ? '✅' : '❌')); console.log(' • 3-Phasen-Verteilung: ' + (MARSTEK_CONFIG.useThreePhases ? '✅' : '❌')); console.log(' • Schnelles Polling: ' + (MARSTEK_CONFIG.fastPolling ? '✅' : '❌')); console.log(' • Wert-Stabilisierung: ' + (MARSTEK_CONFIG.stabilizeSmallValues ? '✅' : '❌')); // Empfehlungen console.log(''); console.log('💡 EMPFEHLUNGEN:'); if (problemCounter > 5) { console.log(' ⚠️ Häufige 797W-Erkennungen → Versuche "aggressive" Modus'); } if (!MARSTEK_CONFIG.forcePositiveValues) { console.log(' 🔧 Aktiviere positive Werte (Community-bewährter Fix)'); } if (UPDATE_INTERVAL > 3000) { console.log(' ⚡ Reduziere Update-Intervall für bessere Reaktion'); } if (Math.abs(currentPowerTotal) > 750 && Math.abs(currentPowerTotal) < 850) { console.log(' 🚨 AKTUELL IM 797W-BEREICH → Beobachte Venus E Verhalten'); } console.log('====================================='); console.log(''); } // ✅ PERFORMANCE-ÜBERWACHUNG function trackPerformance() { var startTime = Date.now(); setInterval(function() { var now = Date.now(); var uptime = Math.floor((now - startTime) / 1000); var requestsPerMinute = Math.floor(requestCounter / (uptime / 60)); if (uptime % 300 === 0 && uptime > 0) { // Alle 5 Minuten console.log('📈 PERFORMANCE (' + uptime + 's Laufzeit):'); console.log(' • Requests/Min: ' + requestsPerMinute); console.log(' • Gesamt-Requests: ' + requestCounter); console.log(' • 797W-Probleme: ' + problemCounter); console.log(' • Letzte Übertragung: ' + currentPowerTotal + 'W'); } }, 1000); } // Monitoring alle 2 Minuten (reduziert für bessere Performance) setInterval(monitorAnti797WBehavior, 120000); // Performance-Tracking starten trackPerformance(); // ✅ AUTOMATISCHE PROBLEMERKENNUNG setInterval(function() { // Automatische Diagnose bei auffälligen Werten if (Math.abs(currentPowerTotal) > 750 && Math.abs(currentPowerTotal) < 850) { if (problemCounter % 10 === 0 && problemCounter > 0) { console.log('🔍 Automatische Diagnose wird ausgeführt...'); setTimeout(diagnoseVenusEProblem, 2000); } } }, 30000); // Alle 30 Sekunden prüfen // ✅ EMULATION STARTEN mit Community-bewährten Einstellungen console.log('🚀 Initialisiere ANTI-797W Optimierungen...'); switchAnti797WMode('community'); // Verwende bewährte Community-Fixes startOptimizedEmulation(); // Cleanup bei Script-Stop onStop(function() { console.log('🛑 Anti-797W Script wird gestoppt...'); console.log('📊 Finale Statistiken:'); console.log(' • Verarbeitete Requests: ' + requestCounter); console.log(' • 797W-Erkennungen: ' + problemCounter); console.log(' • Letzter Wert: ' + currentPowerTotal + 'W'); stopEmulation(); }); // ✅ INTERAKTIVE FUNKTIONEN: // Verwende diese Funktionen in der ioBroker-Konsole: /* VERFÜGBARE BEFEHLE: 1. switchAnti797WMode('aggressive') → Maximale Anti-797W Fixes 2. switchAnti797WMode('community') → Bewährte Community-Fixes 3. switchAnti797WMode('conservative') → Minimale Änderungen 4. switchAnti797WMode('debug') → Maximales Logging 5. switchAnti797WMode('original') → Wie das ursprüngliche Script 6. diagnoseVenusEProblem() → Vollständige Problemanalyse 7. monitorAnti797WBehavior() → Sofortiges Status-Update SETUP-ANLEITUNG: 1. Ersetze 'XXXXXXXXXXXXXX' mit deiner echten Shelly EM3 Pro ID 2. Passe DEINE-IP und DEIN-WIFI-NAME an deine Umgebung an 3. Starte das Script in ioBroker BEISPIEL: - Wenn Venus E immer noch 797W zeigt → switchAnti797WMode('aggressive') - Für Problemanalyse → diagnoseVenusEProblem() - Für mehr Details → switchAnti797WMode('debug') */ console.log(''); console.log('🎯 === ANTI-797W SCRIPT BEREIT ==='); console.log('💡 Verfügbare Befehle siehe Script-Kommentare'); console.log('🔍 Für Diagnose: diagnoseVenusEProblem()'); console.log('⚙️ Für andere Modi: switchAnti797WMode("aggressive")'); console.log('=====================================');
-
Nun kommen alle drei Phasen in der App an.
Weis aber noch nicht wie sich der Venus-E verhält, aktuelle sind die Speicher voll, und haben PV-Überschuss.// =================================================== // Shelly Pro 3EM Emulation für Marstek Venus E // by Gismoh // =================================================== // // ZWECK: Emuliert einen echten Shelly Pro 3EM für Marstek Venus E Batteriespeicher // // FUNKTIONEN: // ✅ 1:1 Shelly Pro 3EM API Emulation (beide Endpunkte) // ✅ Verwendet echte 3-Phasen Messwerte vom Shelly 3EM // ✅ Tibber Pulse als primäre Datenquelle // ✅ HTTP + UDP Server für Venus E Kompatibilität // ✅ Reduziertes Logging für bessere Übersicht // // GETESTET MIT: // - Marstek Venus E (Firmware V152) // - ioBroker Shelly Adapter // - Tibber Pulse Stromzähler // // INSTALLATION: // 1. Shelly 3EM Instanz ID anpassen // 2. Tibber Pulse verfügbar? (sonst anpassen) // 3. Script in ioBroker JavaScript speichern // // =================================================== // KONFIGURATION - HIER ANPASSEN! var MARSTEK_CONFIG = { invertSignForTest: false, // Vorzeichen normal lassen debugMode: false, // Reduziertes Logging logInterval: 30000, // Nur alle 30 Sekunden loggen minChangeThreshold: 50 // Nur bei >50W Änderung loggen }; // WICHTIG: Ihre Shelly 3EM Instanz ID hier eintragen! var SHELLY_EM3_INSTANCE = 'shelly.0.SHEM-3#XXXXXXXXXXXX#1'; var EMULATION_PORT = 1010; // Port für Venus E var UPDATE_INTERVAL = 5000; // Update alle 5 Sekunden // Module laden var http = require('http'); var url = require('url'); var dgram = require('dgram'); var os = require('os'); // Globale Variablen für Messwerte var currentPowerTotal = 0; var currentPowerL1 = 0; var currentPowerL2 = 0; var currentPowerL3 = 0; // Echte Messwerte für alle 3 Phasen var currentCurrentL1 = 0; var currentCurrentL2 = 0; var currentCurrentL3 = 0; var currentVoltageL1 = 230.0; var currentVoltageL2 = 230.0; var currentVoltageL3 = 230.0; var currentPowerFactorL1 = 1.0; var currentPowerFactorL2 = 1.0; var currentPowerFactorL3 = 1.0; var energyTotal = 0; var energyL1 = 0; var energyL2 = 0; var energyL3 = 0; // Performance-Variablen var lastLogTime = 0; var requestCounter = 0; var lastLoggedPower = 0; var lastDetailedLog = 0; // Server-Instanzen var serverInstance = null; var udpSocket = null; function calculateMarstekPower(originalPower) { var result = { originalPower: originalPower, marstekPower: originalPower, direction: originalPower >= 0 ? 'Netzbezug' : 'Einspeisung', explanation: '' }; // Begrenzung für Akkuverhalten (optional) if (originalPower < 0) { result.explanation = 'Einspeisung - Venus E sollte laden'; } else if (originalPower > 0) { result.explanation = 'Netzbezug - Venus E sollte entladen'; } else { result.explanation = 'Kein Netzfluss'; } return result; } // Shelly 3EM und Tibber Pulse Werte auslesen function readShellyValues() { try { // HAUPTQUELLE: Tibber Pulse (direkt am Stromzähler) var tibberPower = getState('tibberlink.0.LocalPulse.0.Power').val; // FALLBACK: Shelly 3EM als Backup var shellyL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val || 0; var shellyL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Power').val || 0; var shellyL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Power').val || 0; var shellyTotal = shellyL1 + shellyL2 + shellyL3; var oldPowerTotal = currentPowerTotal; // Tibber als Hauptquelle, Shelly als Fallback if (tibberPower !== null && tibberPower !== undefined && Math.abs(tibberPower) < 10000) { currentPowerTotal = tibberPower; } else { currentPowerTotal = shellyTotal; console.log('⚠️ FALLBACK zu Shelly: ' + shellyTotal + 'W (Tibber: ' + tibberPower + ')'); } // Logging bei größeren Änderungen var powerChange = Math.abs(currentPowerTotal - oldPowerTotal); if (powerChange > MARSTEK_CONFIG.minChangeThreshold) { console.log('📊 POWER UPDATE: ' + oldPowerTotal + 'W → ' + currentPowerTotal + 'W (Δ' + powerChange + 'W)'); } // Echte Einzelphasen-Werte auslesen currentPowerL1 = shellyL1; currentPowerL2 = shellyL2; currentPowerL3 = shellyL3; // Echte Ströme, Spannungen und PowerFactor auslesen try { currentCurrentL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Current').val || Math.abs(shellyL1 / 230); currentCurrentL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Current').val || Math.abs(shellyL2 / 230); currentCurrentL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Current').val || Math.abs(shellyL3 / 230); currentVoltageL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Voltage').val || 230.0; currentVoltageL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Voltage').val || 230.0; currentVoltageL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Voltage').val || 230.0; // ACHTUNG: Alle drei heißen "PowerFactor" (mit großem F) currentPowerFactorL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.PowerFactor').val || (shellyL1 >= 0 ? 1.0 : -1.0); currentPowerFactorL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.PowerFactor').val || (shellyL2 >= 0 ? 1.0 : -1.0); currentPowerFactorL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.PowerFactor').val || (shellyL3 >= 0 ? 1.0 : -1.0); } catch (e) { // Fallback zu berechneten Werten currentCurrentL1 = Math.abs(shellyL1 / 230); currentCurrentL2 = Math.abs(shellyL2 / 230); currentCurrentL3 = Math.abs(shellyL3 / 230); currentVoltageL1 = 230.0; currentVoltageL2 = 230.0; currentVoltageL3 = 230.0; currentPowerFactorL1 = shellyL1 >= 0 ? 1.0 : -1.0; currentPowerFactorL2 = shellyL2 >= 0 ? 1.0 : -1.0; currentPowerFactorL3 = shellyL3 >= 0 ? 1.0 : -1.0; } // Energie auslesen energyL1 = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total').val || 0; energyL2 = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Total').val || 0; energyL3 = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Total').val || 0; energyTotal = energyL1 + energyL2 + energyL3; } catch (error) { console.error('❌ Fehler beim Lesen der Shelly Werte: ' + error); } } // Lokale IP-Adresse ermitteln function getLocalIP() { try { var interfaces = os.networkInterfaces(); for (var interfaceName in interfaces) { var addresses = interfaces[interfaceName]; for (var i = 0; i < addresses.length; i++) { var address = addresses[i]; if (address.family === 'IPv4' && !address.internal) { return address.address; } } } } catch (e) { console.log('Fehler beim Ermitteln der IP-Adresse'); } return '192.168.1.100'; } // ECHTE Shelly Pro 3EM EM.GetStatus Response (für Venus E) function createEMGetStatusResponse() { var demand = currentPowerTotal; var correctedPower = demand; var powerCalc = calculateMarstekPower(correctedPower); if (MARSTEK_CONFIG.debugMode && Math.abs(correctedPower - lastLoggedPower) > MARSTEK_CONFIG.minChangeThreshold) { console.log('🎯 EM.GetStatus ECHTE WERTE: A=' + currentPowerL1.toFixed(1) + 'W/' + currentCurrentL1.toFixed(2) + 'A, B=' + currentPowerL2.toFixed(1) + 'W/' + currentCurrentL2.toFixed(2) + 'A, C=' + currentPowerL3.toFixed(1) + 'W/' + currentCurrentL3.toFixed(2) + 'A'); } // ECHTE Shelly Pro 3EM Struktur - 1:1 wie Original! return { "id": 0, "a_current": currentCurrentL1, "a_voltage": currentVoltageL1, "a_act_power": currentPowerL1, "a_aprt_power": Math.abs(currentPowerL1), "a_pf": currentPowerFactorL1, "a_freq": 50.0, "b_current": currentCurrentL2, "b_voltage": currentVoltageL2, "b_act_power": currentPowerL2, "b_aprt_power": Math.abs(currentPowerL2), "b_pf": currentPowerFactorL2, "b_freq": 50.0, "c_current": currentCurrentL3, "c_voltage": currentVoltageL3, "c_act_power": currentPowerL3, "c_aprt_power": Math.abs(currentPowerL3), "c_pf": currentPowerFactorL3, "c_freq": 50.0, "n_current": null, // Wie echter Shelly "total_current": currentCurrentL1 + currentCurrentL2 + currentCurrentL3, "total_act_power": powerCalc.marstekPower, // KRITISCH für Venus E! "total_aprt_power": Math.abs(powerCalc.marstekPower), "user_calibrated_phase": [], "errors": [] }; } // ECHTE Shelly.GetStatus Response (Legacy Support) function createShellyGetStatusResponse() { var demand = currentPowerTotal; var correctedPower = demand; var powerCalc = calculateMarstekPower(correctedPower); if (MARSTEK_CONFIG.debugMode && Math.abs(correctedPower - lastLoggedPower) > MARSTEK_CONFIG.minChangeThreshold) { console.log('📊 Shelly.GetStatus ECHTE WERTE: A=' + currentPowerL1.toFixed(1) + 'W/' + currentCurrentL1.toFixed(2) + 'A, B=' + currentPowerL2.toFixed(1) + 'W/' + currentCurrentL2.toFixed(2) + 'A, C=' + currentPowerL3.toFixed(1) + 'W/' + currentCurrentL3.toFixed(2) + 'A'); } // ECHTE Shelly.GetStatus Struktur - mit em:0 Verschachtelung return { "ble": {}, "cloud": {"connected": true}, "em:0": { "id": 0, "a_current": currentCurrentL1, "a_voltage": currentVoltageL1, "a_act_power": currentPowerL1, "a_aprt_power": Math.abs(currentPowerL1), "a_pf": currentPowerFactorL1, "a_freq": 50.0, "b_current": currentCurrentL2, "b_voltage": currentVoltageL2, "b_act_power": currentPowerL2, "b_aprt_power": Math.abs(currentPowerL2), "b_pf": currentPowerFactorL2, "b_freq": 50.0, "c_current": currentCurrentL3, "c_voltage": currentVoltageL3, "c_act_power": currentPowerL3, "c_aprt_power": Math.abs(currentPowerL3), "c_pf": currentPowerFactorL3, "c_freq": 50.0, "n_current": null, "total_current": currentCurrentL1 + currentCurrentL2 + currentCurrentL3, "total_act_power": powerCalc.marstekPower, "total_aprt_power": Math.abs(powerCalc.marstekPower), "user_calibrated_phase": [] }, "emdata:0": { "id": 0, "a_total_act_energy": energyL1, "a_total_act_ret_energy": 0, "b_total_act_energy": energyL2, "b_total_act_ret_energy": 0, "c_total_act_energy": energyL3, "c_total_act_ret_energy": 0, "total_act": energyTotal, "total_act_ret": 0 }, "eth": {"ip": getLocalIP()}, "modbus": {}, "mqtt": {"connected": false}, "sys": { "mac": "A8032AB12345", "restart_required": false, "time": new Date().toTimeString().split(' ')[0], "unixtime": Math.floor(Date.now() / 1000), "uptime": 12345, "ram_size": 255736, "ram_free": 76476, "fs_size": 524288, "fs_free": 188416, "cfg_rev": 12, "kvs_rev": 1 } }; } // HTTP Server für Venus E var server = http.createServer(function(req, res) { var parsedUrl = url.parse(req.url, true); // CORS Headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // Nur wichtige HTTP Requests loggen var isImportantRequest = parsedUrl.pathname.includes('/rpc/EM.GetStatus') || parsedUrl.pathname.includes('/rpc/Shelly.GetStatus') || parsedUrl.pathname.includes('/settings'); if (isImportantRequest) { console.log('🌐 HTTP: ' + parsedUrl.pathname + ' von ' + req.connection.remoteAddress); } // KRITISCH: Venus E erwartet /rpc/EM.GetStatus → DIREKTE Struktur if (parsedUrl.pathname === '/rpc/EM.GetStatus') { res.writeHead(200, { 'Content-Type': 'application/json' }); var response = createEMGetStatusResponse(); var powerChange = Math.abs(response.total_act_power - lastLoggedPower); if (powerChange > MARSTEK_CONFIG.minChangeThreshold) { console.log('🎯 VENUS E REQUEST! total_act_power: ' + response.total_act_power + 'W (Δ' + powerChange + 'W)'); lastLoggedPower = response.total_act_power; } res.end(JSON.stringify(response)); return; } // Legacy Support: /rpc/Shelly.GetStatus → VERSCHACHTELTE Struktur if (parsedUrl.pathname === '/rpc/Shelly.GetStatus' || parsedUrl.pathname === '/status') { res.writeHead(200, { 'Content-Type': 'application/json' }); var response = createShellyGetStatusResponse(); var powerChange = Math.abs(response['em:0'].total_act_power - lastLoggedPower); if (powerChange > MARSTEK_CONFIG.minChangeThreshold) { console.log('📊 Legacy Shelly.GetStatus! em:0.total_act_power: ' + response['em:0'].total_act_power + 'W'); } res.end(JSON.stringify(response)); return; } // Shelly Info API if (parsedUrl.pathname === '/rpc/Shelly.GetInfo' || parsedUrl.pathname === '/settings') { res.writeHead(200, { 'Content-Type': 'application/json' }); var infoResponse = { "id": 0, "src": "shellypro3em-a8032ab12345", "result": { "name": "Shelly Pro 3EM Emulation (Forum Version)", "id": "shellypro3em-a8032ab12345", "mac": "A8032AB12345", "slot": 1, "model": "SPRO-3EM", "gen": 2, "fw_id": "20230913-114414/v1.14.0-gcb84623", "ver": "1.14.0", "app": "Pro3EM", "auth_en": false, "auth_domain": null } }; res.end(JSON.stringify(infoResponse)); return; } // Weitere Shelly-Endpunkte if (parsedUrl.pathname === '/shelly') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ "type": "SPRO-3EM", "mac": "A8032AB12345", "auth": false, "fw": "1.14.0", "discoverable": true, "longid": 1, "num_outputs": 1, "num_meters": 3 })); return; } // RPC Fallback if (parsedUrl.pathname === '/rpc') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(createEMGetStatusResponse())); return; } // Device Description für Discovery if (parsedUrl.pathname === '/description.xml') { res.writeHead(200, { 'Content-Type': 'text/xml' }); var xml = '<?xml version="1.0"?>\n' + '<root xmlns="urn:schemas-upnp-org:device-1-0">\n' + '<device>\n' + '<deviceType>urn:shelly:device:pro3em:1</deviceType>\n' + '<friendlyName>Shelly Pro 3EM Emulation</friendlyName>\n' + '<manufacturer>Allterco</manufacturer>\n' + '<modelName>Shelly Pro 3EM</modelName>\n' + '<UDN>uuid:shelly-pro3em-emulation</UDN>\n' + '</device>\n' + '</root>'; res.end(xml); return; } // 404 für unbekannte Requests res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found: ' + parsedUrl.pathname); }); // UDP RPC Request Handler für Venus E function handleRPCRequest(msg, rinfo, socket) { var message = msg.toString(); try { if (message.includes('EM.GetStatus') || message.includes('Shelly.GetStatus')) { var correctedPower = currentPowerTotal; var powerCalc = calculateMarstekPower(correctedPower); requestCounter++; var now = Date.now(); // Reduziertes Logging - nur alle 30 Sekunden oder bei großen Änderungen var powerChange = Math.abs(powerCalc.marstekPower - lastLoggedPower); var shouldLog = (now - lastDetailedLog > MARSTEK_CONFIG.logInterval) || (powerChange > MARSTEK_CONFIG.minChangeThreshold); if (shouldLog) { var methodType = message.includes('EM.GetStatus') ? 'EM.GetStatus' : 'Shelly.GetStatus'; console.log('📡 UDP #' + requestCounter + ' (' + methodType + ') von ' + rinfo.address + ' → ' + powerCalc.marstekPower + 'W'); lastDetailedLog = now; lastLoggedPower = powerCalc.marstekPower; } var requestData; try { requestData = JSON.parse(message); } catch (e) { requestData = { id: 1, method: 'EM.GetStatus', params: { id: 0 } }; } // Verschiedene RPC Responses je nach Method var rpcResponse; if (message.includes('EM.GetStatus')) { // Venus E UDP Request → DIREKTE Struktur rpcResponse = { "id": requestData.id || 1, "src": "shellypro3em-a8032ab12345", "result": createEMGetStatusResponse() }; } else { // Legacy UDP Request → VERSCHACHTELTE Struktur rpcResponse = { "id": requestData.id || 1, "src": "shellypro3em-a8032ab12345", "result": createShellyGetStatusResponse() }; } var responseStr = JSON.stringify(rpcResponse); socket.send(responseStr, rinfo.port, rinfo.address, function(err) { if (err) { console.log('❌ UDP Send Error: ' + err); } }); } else { try { var rpcRequest = JSON.parse(message); if (rpcRequest.method) { console.log('🔧 RPC Method: ' + rpcRequest.method); var rpcResponse = { "id": rpcRequest.id || 1, "src": "shellypro3em-emulation", "result": rpcRequest.method === 'EM.GetStatus' ? createEMGetStatusResponse() : createShellyGetStatusResponse() }; var responseStr = JSON.stringify(rpcResponse); socket.send(responseStr, rinfo.port, rinfo.address); console.log('📤 JSON RPC Response gesendet'); } } catch (e) { if (requestCounter % 100 === 0) { console.log('❓ Unbekannte UDP-Nachricht (alle 100 Requests): ' + message.substring(0, 50) + '...'); } } } } catch (error) { console.log('❌ UDP Error: ' + error); } } // UDP Socket für Discovery function createUDPSocket() { udpSocket = dgram.createSocket('udp4'); udpSocket.on('message', function(msg, rinfo) { var message = msg.toString(); // Discovery Response if (message.includes('M-SEARCH') || message.includes('shelly') || message.includes('SSDP')) { var localIP = getLocalIP(); var response = 'HTTP/1.1 200 OK\r\n' + 'ST: urn:shelly:device\r\n' + 'USN: uuid:shelly-pro3em-emulation\r\n' + 'LOCATION: http://' + localIP + ':' + EMULATION_PORT + '/settings\r\n' + 'SERVER: Shelly/1.14.0\r\n' + 'CACHE-CONTROL: max-age=1800\r\n' + '\r\n'; udpSocket.send(response, rinfo.port, rinfo.address); } else { handleRPCRequest(msg, rinfo, udpSocket); } }); udpSocket.on('error', function(err) { console.log('UDP Discovery Fehler: ' + err); }); } // Emulation starten function startEmulation() { if (serverInstance) { console.log('Emulation läuft bereits'); return; } try { // HTTP Server starten serverInstance = server.listen(EMULATION_PORT, function() { console.log('=== SHELLY PRO 3EM EMULATION GESTARTET ==='); console.log('Port: ' + EMULATION_PORT); console.log('Erreichbar unter: http://localhost:' + EMULATION_PORT); console.log(''); console.log('✅ ECHTE API STRUKTUR:'); console.log(' /rpc/EM.GetStatus → DIREKTE Struktur (für Venus E)'); console.log(' /rpc/Shelly.GetStatus → VERSCHACHTELTE Struktur (Legacy)'); console.log(''); console.log('🎯 Venus E sollte /rpc/EM.GetStatus verwenden!'); console.log('📊 Verwendet echte 3-Phasen Messwerte vom Shelly 3EM'); console.log('⚡ Tibber Pulse als primäre Stromquelle'); console.log('========================================================'); }); // UDP Discovery starten createUDPSocket(); udpSocket.bind(1900, function() { console.log('UDP Discovery läuft auf Port 1900'); }); // UDP RPC Socket var rpcSocket = dgram.createSocket('udp4'); rpcSocket.bind(1010, function() { console.log('UDP RPC Socket läuft auf Port 1010'); }); rpcSocket.on('message', function(msg, rinfo) { handleRPCRequest(msg, rinfo, rpcSocket); }); rpcSocket.on('error', function(err) { console.log('UDP RPC Port 1010 Fehler: ' + err); }); // Messwerte regelmäßig updaten setInterval(readShellyValues, UPDATE_INTERVAL); // Initiale Werte laden readShellyValues(); } catch (error) { console.error('Fehler beim Starten der Emulation:'); console.error(error); } } function stopEmulation() { if (serverInstance) { serverInstance.close(function() { console.log('Shelly EM3 Pro Emulation gestoppt'); serverInstance = null; }); } if (udpSocket) { udpSocket.close(function() { console.log('UDP Discovery gestoppt'); }); } } // Monitoring-Funktion function monitorMarstekBehavior() { console.log('\n=== MARSTEK STATUS UPDATE ==='); console.log('⚡ Aktueller Verbrauch: ' + currentPowerTotal + 'W'); console.log('📊 UDP Requests: ' + requestCounter + ' (seit Start)'); console.log('🔧 API-Struktur: EM.GetStatus (DIREKT) + Shelly.GetStatus (VERSCHACHTELT)'); if (currentPowerTotal !== 0) { var calc = calculateMarstekPower(currentPowerTotal); console.log('🎯 Venus E erhält: total_act_power=' + calc.marstekPower + 'W (' + calc.direction + ')'); } // Reset Counter für bessere Übersicht if (requestCounter > 1000) { requestCounter = 0; console.log('🔄 Request Counter zurückgesetzt'); } console.log('=============================\n'); } // Monitoring alle 2 Minuten setInterval(monitorMarstekBehavior, 120000); // Emulation starten startEmulation(); // Cleanup bei Script-Stop onStop(function() { console.log('Script wird gestoppt...'); stopEmulation(); }); // Test-Funktion für Venus E Kompatibilität function testVenusECompatibility() { console.log('\n=== VENUS E KOMPATIBILITÄTS-TEST ==='); console.log('🎯 Teste EM.GetStatus (Venus E Endpoint):'); var emResponse = createEMGetStatusResponse(); console.log('- total_act_power: ' + (emResponse.total_act_power !== undefined ? '✅ ' + emResponse.total_act_power + 'W' : '❌ FEHLT')); console.log('- id: ' + (emResponse.id !== undefined ? '✅ ' + emResponse.id : '❌ FEHLT')); console.log('- a_act_power: ' + (emResponse.a_act_power !== undefined ? '✅ ' + emResponse.a_act_power + 'W' : '❌ FEHLT')); console.log('- Struktur: ' + (emResponse['em:0'] === undefined ? '✅ DIREKT (korrekt für Venus E)' : '❌ VERSCHACHTELT')); console.log('\n📊 Teste Shelly.GetStatus (Legacy Endpoint):'); var shellyResponse = createShellyGetStatusResponse(); console.log('- em:0.total_act_power: ' + (shellyResponse['em:0'] && shellyResponse['em:0'].total_act_power !== undefined ? '✅ ' + shellyResponse['em:0'].total_act_power + 'W' : '❌ FEHLT')); console.log('- Struktur: ' + (shellyResponse['em:0'] !== undefined ? '✅ VERSCHACHTELT (korrekt für Legacy)' : '❌ NICHT VERSCHACHTELT')); console.log('\n💡 LOGGING KONFIGURATION:'); console.log('- Debug Mode: ' + (MARSTEK_CONFIG.debugMode ? '🔍 AN (ausführlich)' : '🔇 AUS (reduziert)')); console.log('- Change Threshold: ' + MARSTEK_CONFIG.minChangeThreshold + 'W (nur bei größeren Änderungen loggen)'); console.log('- Log Interval: ' + (MARSTEK_CONFIG.logInterval / 1000) + 's (reduziert Spam)'); console.log('====================================\n'); } // Test bei Start ausführen setTimeout(testVenusECompatibility, 3000); // DEBUGGING: Detailed Logging ein-/ausschalten function enableDetailedLogging() { MARSTEK_CONFIG.debugMode = true; MARSTEK_CONFIG.minChangeThreshold = 0; // Jede Änderung loggen MARSTEK_CONFIG.logInterval = 5000; // Alle 5 Sekunden console.log('🔍 DETAILED LOGGING AKTIVIERT'); console.log('- Jede Wert-Änderung wird geloggt'); console.log('- Alle HTTP/UDP Requests werden geloggt'); console.log('- Aufruf: disableDetailedLogging() zum Deaktivieren'); } function disableDetailedLogging() { MARSTEK_CONFIG.debugMode = false; MARSTEK_CONFIG.minChangeThreshold = 50; // Nur große Änderungen MARSTEK_CONFIG.logInterval = 30000; // Alle 30 Sekunden console.log('🔇 DETAILED LOGGING DEAKTIVIERT'); console.log('- Nur noch wichtige Änderungen werden geloggt'); console.log('- Reduzierte Log-Ausgabe für bessere Übersicht'); } // =================================================== // VERWENDETE DATENPUNKTE (für Forum-User): // =================================================== // // SHELLY 3EM DATENPUNKTE: // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter0.Power (Phase A Leistung) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter0.Current (Phase A Strom) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter0.Voltage (Phase A Spannung) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter0.PowerFactor (Phase A PowerFactor) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter0.Total (Phase A Energie) // // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter1.Power (Phase B Leistung) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter1.Current (Phase B Strom) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter1.Voltage (Phase B Spannung) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter1.PowerFactor (Phase B PowerFactor) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter1.Total (Phase B Energie) // // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter2.Power (Phase C Leistung) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter2.Current (Phase C Strom) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter2.Voltage (Phase C Spannung) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter2.PowerFactor (Phase C PowerFactor) // shelly.0.SHEM-3#XXXXXXXXXXXX#1.Emeter2.Total (Phase C Energie) // // TIBBER PULSE DATENPUNKT: // tibberlink.0.LocalPulse.0.Power (Gesamt-Leistung) // // =================================================== // INSTALLATION & KONFIGURATION: // =================================================== // // 1. Zeile 31: Ihre Shelly 3EM Instanz ID eintragen // Format: shelly.0.SHEM-3#XXXXXXXXXXXXXX#1 // Zu finden in: ioBroker Admin → Objekte → shelly.0 // // 2. Zeile 95: Tibber Pulse Datenpunkt prüfen // Falls nicht vorhanden: Zeile auskommentieren // // 3. Script in ioBroker JavaScript als "Allgemein" speichern // // 4. Venus E Konfiguration: // - Eigenverbrauch-Modus aktivieren // - CT-Typ: Shelly Pro 3EM // - IP-Adresse: Ihre ioBroker IP // - Port: 1010 // // =================================================== // FUNKTIONSWEISE: // =================================================== // // Das Script emuliert einen echten Shelly Pro 3EM mit: // - Korrekter API-Struktur (beide Endpunkte) // - Echten 3-Phasen Messwerten // - HTTP + UDP Server // - Discovery-Protokoll // // Venus E erkennt das Script als echten Shelly Pro 3EM // und kann damit die Nulleinspeisung/Eigenverbrauch steuern. // // VORZEICHEN-BEDEUTUNG: // +1000W = 1kW Netzbezug → Venus E sollte entladen // -1000W = 1kW Einspeisung → Venus E sollte laden // // =================================================== // TROUBLESHOOTING: // =================================================== // // Problem: Venus E reagiert nicht // Lösung: enableDetailedLogging() aufrufen für mehr Logs // // Problem: Falsche Werte // Lösung: Shelly 3EM Datenpunkte in ioBroker prüfen // // Problem: Keine Verbindung // Lösung: Port 1010 in Firewall freigeben // // Problem: Script-Fehler // Lösung: ioBroker Log prüfen, Datenpunkt-Namen kontrollieren // // ===================================================
-
LoL, habe nun mal den anderen Marstek Venus e auf Eigenverbrauch gesetzt, dort regelt der Mustek nun selbstständig
Muss nur noch beobachten wie, aber denke darauf habe ich mit echten Werten eh keinen Einfluss,
aber der JS scheint auszureichen.// =================================================== // Shelly EM3 Pro Emulation für Marstek Venus E (MIT ECHTEN SHELLY-WERTEN!) // by Gismoh // =================================================== // MARSTEK KONFIGURATION - HIER ANPASSEN! var MARSTEK_CONFIG = { useAbsoluteValues: false, // Für CT001 Protokoll-Kompatibilität invertSignForTest: false, // ✅ RICHTIGE VORZEICHEN (wie echter Shelly!) - TESTWEISE AUF TRUE SETZEN! clampNegativeToZero: false, // Alternative: negative Werte auf 0 separateInputOutput: true, // Separate Input/Output Behandlung debugMode: false, // ❌ Debug-Modus DEAKTIVIERT (reduziert Spam) logInterval: 30000, // Nur alle 30 Sekunden loggen logOnlyChanges: true, // Nur bei Wert-Änderungen loggen minChangeThreshold: 50 // Mindestens 50W Änderung für Log }; // Konfiguration - Anpassen an deine Shelly EM3 Instanz var SHELLY_EM3_INSTANCE = 'shelly.0.SHEM-3#XXXXXXXXXX'; // Deine Shelly EM3 Instanz ID var EMULATION_PORT = 1010; // Port für die Emulation var UPDATE_INTERVAL = 5000; // Update-Intervall in ms // Module laden var http = require('http'); var url = require('url'); var dgram = require('dgram'); var os = require('os'); // Globale Variablen für die Messwerte var currentPowerTotal = 0; var currentPowerL1 = 0; var currentPowerL2 = 0; var currentPowerL3 = 0; // ✅ ECHTE MESSWERTE FÜR ALLE PHASEN (AUS SHELLY DATEN!) var currentCurrentL1 = 0; var currentCurrentL2 = 0; var currentCurrentL3 = 0; var currentVoltageL1 = 230.0; var currentVoltageL2 = 230.0; var currentVoltageL3 = 230.0; var currentPowerFactorL1 = 1.0; var currentPowerFactorL2 = 1.0; var currentPowerFactorL3 = 1.0; var energyTotal = 0; var energyL1 = 0; var energyL2 = 0; var energyL3 = 0; var energyReturnedL1 = 0; var energyReturnedL2 = 0; var energyReturnedL3 = 0; // Performance-Variablen var lastLogTime = 0; var requestCounter = 0; var lastLoggedPower = 0; // ✅ Für Change-Detection var lastDetailedLog = 0; // ✅ Für seltene Detail-Logs // Server-Instanzen var serverInstance = null; var udpSocket = null; function calculateMarstekPower(originalPower) { var result = { originalPower: originalPower, marstekPower: originalPower, // Für CT-Anzeige (immer Echtwert) limitedPower: originalPower, // Wird unten korrigiert powerInput: 0, powerOutput: 0, direction: originalPower >= 0 ? 'Bezug' : 'Einspeisung', explanation: '' }; // ✅ VORZEICHEN UMKEHREN FÜR TEST if (MARSTEK_CONFIG.invertSignForTest) { result.marstekPower = -originalPower; result.direction = result.marstekPower >= 0 ? 'Bezug (umgekehrt)' : 'Einspeisung (umgekehrt)'; result.explanation = 'Vorzeichen umgekehrt für Venus E Test'; } // Begrenzung für Akkuverhalten (auf umgekehrten Wert anwenden) var powerForLimit = result.marstekPower; if (powerForLimit < 0) { // Einspeisung → Akku darf max. mit 1000 W laden result.limitedPower = Math.max(powerForLimit, -1000); result.explanation += ' - Ladebegrenzung auf max 1000W'; } else if (powerForLimit > 0) { // Netzbezug → Akku darf max. mit 1000 W entladen result.limitedPower = Math.min(powerForLimit, 1000); result.explanation += ' - Entladebegrenzung auf max 1000W'; } else { result.limitedPower = 0; result.explanation += ' - Kein Netzfluss – keine Aktion'; } if (MARSTEK_CONFIG.separateInputOutput) { if (result.marstekPower >= 0) { result.powerInput = result.marstekPower; result.powerOutput = 0; } else { result.powerInput = 0; result.powerOutput = Math.abs(result.marstekPower); } } // ✅ NUR LOGGEN BEI GROßEN ÄNDERUNGEN var powerChange = Math.abs(result.marstekPower - lastLoggedPower); if (MARSTEK_CONFIG.debugMode && powerChange > MARSTEK_CONFIG.minChangeThreshold) { console.log('POWER CHANGE: ' + lastLoggedPower + 'W → ' + result.marstekPower + 'W (' + result.direction + ')'); lastLoggedPower = result.marstekPower; } return result; } // ✅ KORRIGIERTE Funktion zum Lesen der ECHTEN Shelly EM3 Pro Werte function readShellyValues() { try { // ✅ HAUPTQUELLE: Tibber Pulse (direkt am Stromzähler) var tibberPower = getState('tibberlink.0.LocalPulse.0.Power').val; // ✅ ECHTE SHELLY EM3 PRO WERTE AUSLESEN var shellyL1Power = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val || 0; var shellyL2Power = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Power').val || 0; var shellyL3Power = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Power').val || 0; // ✅ ECHTE CURRENT-WERTE (Ampere) var shellyL1Current = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Current').val || 0; var shellyL2Current = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Current').val || 0; var shellyL3Current = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Current').val || 0; // ✅ ECHTE POWER FACTOR WERTE var shellyL1PF = getState(SHELLY_EM3_INSTANCE + '.Emeter0.PowerFactor').val || 1.0; var shellyL2PF = getState(SHELLY_EM3_INSTANCE + '.Emeter1.PowerFactor').val || 1.0; var shellyL3PF = getState(SHELLY_EM3_INSTANCE + '.Emeter2.PowerFactor').val || 1.0; // ✅ ECHTE ENERGIE-WERTE (Wh) var shellyL1Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total').val || 0; var shellyL2Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Total').val || 0; var shellyL3Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Total').val || 0; // ✅ ECHTE RÜCKSPEISUNG-WERTE (Wh) var shellyL1Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total_Returned').val || 0; var shellyL2Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter1.Total_Returned').val || 0; var shellyL3Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter2.Total_Returned').val || 0; var shellyTotal = shellyL1Power + shellyL2Power + shellyL3Power; // ✅ PLAUSIBILITÄTSPRÜFUNG für Shelly-Werte if (Math.abs(shellyL1Power) > 5000 || Math.abs(shellyL2Power) > 5000 || Math.abs(shellyL3Power) > 5000) { console.log('⚠️ Unplausible Shelly-Werte detected: L1=' + shellyL1Power + ', L2=' + shellyL2Power + ', L3=' + shellyL3Power); return; // Keine Update bei unrealistischen Werten } // ✅ SICHERHEITSPRÜFUNG: Plausible Werte verwenden if (tibberPower !== null && tibberPower !== undefined && Math.abs(tibberPower) < 15000) { currentPowerTotal = tibberPower; if (MARSTEK_CONFIG.debugMode) { console.log('Verwende Tibber Pulse: ' + tibberPower + 'W'); } } else { currentPowerTotal = shellyTotal; console.log('FALLBACK zu Shelly: ' + shellyTotal + 'W (Tibber: ' + tibberPower + ')'); } // ✅ ALLE ECHTEN WERTE ÜBERNEHMEN currentPowerL1 = shellyL1Power; currentPowerL2 = shellyL2Power; currentPowerL3 = shellyL3Power; currentCurrentL1 = shellyL1Current; currentCurrentL2 = shellyL2Current; currentCurrentL3 = shellyL3Current; currentPowerFactorL1 = shellyL1PF; currentPowerFactorL2 = shellyL2PF; currentPowerFactorL3 = shellyL3PF; // ✅ VOLTAGE BERECHNUNG (falls verfügbar, sonst aus Power/Current) if (shellyL1Current > 0.1) { currentVoltageL1 = Math.abs(shellyL1Power / shellyL1Current / shellyL1PF); } else { currentVoltageL1 = 230.0; // Fallback } if (shellyL2Current > 0.1) { currentVoltageL2 = Math.abs(shellyL2Power / shellyL2Current / shellyL2PF); } else { currentVoltageL2 = 230.0; // Fallback } if (shellyL3Current > 0.1) { currentVoltageL3 = Math.abs(shellyL3Power / shellyL3Current / shellyL3PF); } else { currentVoltageL3 = 230.0; // Fallback } // ✅ ENERGIE-WERTE energyL1 = shellyL1Energy; energyL2 = shellyL2Energy; energyL3 = shellyL3Energy; energyTotal = energyL1 + energyL2 + energyL3; energyReturnedL1 = shellyL1Returned; energyReturnedL2 = shellyL2Returned; energyReturnedL3 = shellyL3Returned; if (MARSTEK_CONFIG.debugMode) { console.log('📊 ECHTE SHELLY WERTE:'); console.log(' Power: L1=' + currentPowerL1.toFixed(1) + 'W, L2=' + currentPowerL2.toFixed(1) + 'W, L3=' + currentPowerL3.toFixed(1) + 'W, Total=' + currentPowerTotal.toFixed(1) + 'W'); console.log(' Current: L1=' + currentCurrentL1.toFixed(2) + 'A, L2=' + currentCurrentL2.toFixed(2) + 'A, L3=' + currentCurrentL3.toFixed(2) + 'A'); console.log(' Voltage: L1=' + currentVoltageL1.toFixed(1) + 'V, L2=' + currentVoltageL2.toFixed(1) + 'V, L3=' + currentVoltageL3.toFixed(1) + 'V'); console.log(' PowerFactor: L1=' + currentPowerFactorL1.toFixed(2) + ', L2=' + currentPowerFactorL2.toFixed(2) + ', L3=' + currentPowerFactorL3.toFixed(2)); } } catch (error) { console.error('❌ Fehler beim Lesen der Shelly Werte:'); console.error(error); } } // Lokale IP-Adresse ermitteln function getLocalIP() { try { var interfaces = os.networkInterfaces(); for (var interfaceName in interfaces) { var addresses = interfaces[interfaceName]; for (var i = 0; i < addresses.length; i++) { var address = addresses[i]; if (address.family === 'IPv4' && !address.internal) { return address.address; } } } } catch (e) { console.log('Fehler beim Ermitteln der IP-Adresse'); } return '192.168.1.100'; } // ✅ MARSTEK VENUS E FORMAT - EXAKT 4 FELDER AUF ROOT-EBENE! function createMarstekVenusEResponse() { var powerCalc = calculateMarstekPower(currentPowerTotal); // ✅ GENAU DAS WAS MARSTEK ERWARTET - NUR 4 FELDER! var response = { "a_act_power": currentPowerL1, "b_act_power": currentPowerL2, "c_act_power": currentPowerL3, "total_act_power": powerCalc.marstekPower }; // ✅ SICHERSTELLEN DASS NUR 4 FELDER GESENDET WERDEN if (Object.keys(response).length !== 4) { console.log('⚠️ WARNUNG: Response hat ' + Object.keys(response).length + ' Felder statt 4!'); } return response; } // ✅ ECHTE Shelly.GetStatus Response (VERSCHACHTELTE Struktur) function createShellyGetStatusResponse() { var demand = currentPowerTotal; var correctedPower = demand; var powerCalc = calculateMarstekPower(correctedPower); // ✅ NUR LOGGEN BEI DEBUG MODE UND GROßEN ÄNDERUNGEN if (MARSTEK_CONFIG.debugMode && Math.abs(correctedPower - lastLoggedPower) > MARSTEK_CONFIG.minChangeThreshold) { console.log('📊 Shelly.GetStatus ECHTE WERTE: A=' + currentPowerL1.toFixed(1) + 'W/' + currentCurrentL1.toFixed(2) + 'A, B=' + currentPowerL2.toFixed(1) + 'W/' + currentCurrentL2.toFixed(2) + 'A, C=' + currentPowerL3.toFixed(1) + 'W/' + currentCurrentL3.toFixed(2) + 'A'); } // ✅ ECHTE SHELLY.GETSTATUS STRUKTUR - MIT 100% ECHTEN 3-PHASEN WERTEN! return { "ble": {}, "cloud": {"connected": true}, "em:0": { "id": 0, "a_current": currentCurrentL1, // ✅ ECHTER Current-Wert Phase A! "a_voltage": currentVoltageL1, // ✅ ECHTER Voltage-Wert Phase A! "a_act_power": currentPowerL1, // ✅ ECHTER Power-Wert Phase A! "a_aprt_power": Math.abs(currentPowerL1), "a_pf": currentPowerFactorL1, // ✅ ECHTER PowerFactor Phase A! "a_freq": 50.0, "b_current": currentCurrentL2, // ✅ ECHTER Current-Wert Phase B! "b_voltage": currentVoltageL2, // ✅ ECHTER Voltage-Wert Phase B! "b_act_power": currentPowerL2, // ✅ ECHTER Power-Wert Phase B! "b_aprt_power": Math.abs(currentPowerL2), "b_pf": currentPowerFactorL2, // ✅ ECHTER PowerFactor Phase B! "b_freq": 50.0, "c_current": currentCurrentL3, // ✅ ECHTER Current-Wert Phase C! "c_voltage": currentVoltageL3, // ✅ ECHTER Voltage-Wert Phase C! "c_act_power": currentPowerL3, // ✅ ECHTER Power-Wert Phase C! "c_aprt_power": Math.abs(currentPowerL3), "c_pf": currentPowerFactorL3, // ✅ ECHTER PowerFactor Phase C! "c_freq": 50.0, "n_current": null, // ✅ Wie echter Shelly (oft null) "total_current": currentCurrentL1 + currentCurrentL2 + currentCurrentL3, // ✅ Summe der echten Ströme "total_act_power": powerCalc.marstekPower, "total_aprt_power": Math.abs(powerCalc.marstekPower), "user_calibrated_phase": [] }, "emdata:0": { "id": 0, "a_total_act_energy": energyL1, // ✅ ECHTE Energie Phase A "a_total_act_ret_energy": energyReturnedL1, // ✅ ECHTE Rückspeisung Phase A "b_total_act_energy": energyL2, // ✅ ECHTE Energie Phase B "b_total_act_ret_energy": energyReturnedL2, // ✅ ECHTE Rückspeisung Phase B "c_total_act_energy": energyL3, // ✅ ECHTE Energie Phase C "c_total_act_ret_energy": energyReturnedL3, // ✅ ECHTE Rückspeisung Phase C "total_act": energyTotal, // ✅ ECHTE Gesamt-Energie "total_act_ret": energyReturnedL1 + energyReturnedL2 + energyReturnedL3 // ✅ ECHTE Gesamt-Rückspeisung }, "eth": {"ip": getLocalIP()}, "modbus": {}, "mqtt": {"connected": false}, "sys": { "mac": "A8032AB12345", "restart_required": false, "time": new Date().toTimeString().split(' ')[0], "unixtime": Math.floor(Date.now() / 1000), "uptime": 12345, "ram_size": 255736, "ram_free": 76476, "fs_size": 524288, "fs_free": 188416, "cfg_rev": 12, "kvs_rev": 1 } }; } // HTTP Server erstellen var server = http.createServer(function(req, res) { var parsedUrl = url.parse(req.url, true); // CORS Headers setzen res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // ✅ NUR WICHTIGE HTTP REQUESTS LOGGEN var isImportantRequest = parsedUrl.pathname.includes('/rpc/EM.GetStatus') || parsedUrl.pathname.includes('/rpc/Shelly.GetStatus') || parsedUrl.pathname.includes('/settings'); if (isImportantRequest) { console.log('🌐 HTTP: ' + parsedUrl.pathname + ' von ' + req.connection.remoteAddress); } // ✅ KRITISCH: VERSCHIEDENE ENDPOINTS = VERSCHIEDENE STRUKTUREN! // Venus E erwartet: /rpc/EM.GetStatus → MARSTEK FORMAT (4 Felder) if (parsedUrl.pathname === '/rpc/EM.GetStatus') { res.writeHead(200, { 'Content-Type': 'application/json' }); var response = createMarstekVenusEResponse(); console.log('🎯 VENUS E EM.GetStatus! Sende MARSTEK FORMAT: total_act_power=' + response.total_act_power + 'W (ROOT-LEVEL)'); res.end(JSON.stringify(response)); return; } // Legacy Support: /rpc/Shelly.GetStatus → VERSCHACHTELTE Struktur if (parsedUrl.pathname === '/rpc/Shelly.GetStatus' || parsedUrl.pathname === '/status') { res.writeHead(200, { 'Content-Type': 'application/json' }); var response = createShellyGetStatusResponse(); // ✅ NUR BEI GROßEN ÄNDERUNGEN LOGGEN var powerChange = Math.abs(response['em:0'].total_act_power - lastLoggedPower); if (powerChange > MARSTEK_CONFIG.minChangeThreshold) { console.log('📊 Legacy Shelly.GetStatus! em:0.total_act_power: ' + response['em:0'].total_act_power + 'W'); } res.end(JSON.stringify(response)); return; } // Shelly Info API emulieren if (parsedUrl.pathname === '/rpc/Shelly.GetInfo' || parsedUrl.pathname === '/settings') { res.writeHead(200, { 'Content-Type': 'application/json' }); var infoResponse = { "id": 0, "src": "shellypro3em-a8032ab12345", "result": { "name": "Shelly Pro 3EM Emulation (ECHTE SHELLY-DATEN)", "id": "shellypro3em-a8032ab12345", "mac": "A8032AB12345", "slot": 1, "model": "SPRO-3EM", "gen": 2, "fw_id": "20230913-114414/v1.14.0-gcb84623", "ver": "1.14.0", "app": "Pro3EM", "auth_en": false, "auth_domain": null } }; res.end(JSON.stringify(infoResponse)); return; } // Zusätzliche Shelly-Endpunkte if (parsedUrl.pathname === '/shelly') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ "type": "SPRO-3EM", "mac": "A8032AB12345", "auth": false, "fw": "1.14.0", "discoverable": true, "longid": 1, "num_outputs": 1, "num_meters": 3 })); return; } // RPC über HTTP (fallback) - AUCH MARSTEK FORMAT if (parsedUrl.pathname === '/rpc') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(createMarstekVenusEResponse())); return; } // Device Description if (parsedUrl.pathname === '/description.xml') { res.writeHead(200, { 'Content-Type': 'text/xml' }); var xml = '<?xml version="1.0"?>\n' + '<root xmlns="urn:schemas-upnp-org:device-1-0">\n' + '<device>\n' + '<deviceType>urn:shelly:device:pro3em:1</deviceType>\n' + '<friendlyName>Shelly Pro 3EM Emulation (ECHTE SHELLY-DATEN)</friendlyName>\n' + '<manufacturer>Allterco</manufacturer>\n' + '<modelName>Shelly Pro 3EM</modelName>\n' + '<UDN>uuid:shelly-pro3em-emulation</UDN>\n' + '</device>\n' + '</root>'; res.end(xml); return; } // Fallback für unbekannte Anfragen // ✅ NUR UNBEKANNTE REQUESTS LOGGEN (reduziert Spam) res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found: ' + parsedUrl.pathname); }); // ✅ KORRIGIERTE RPC Request Handler für UDP function handleRPCRequest(msg, rinfo, socket) { var message = msg.toString(); try { // RPC-Anfrage parsen if (message.includes('EM.GetStatus') || message.includes('Shelly.GetStatus')) { var correctedPower = currentPowerTotal; var powerCalc = calculateMarstekPower(correctedPower); requestCounter++; var now = Date.now(); // Reduziertes Logging if (now - lastLogTime > MARSTEK_CONFIG.logInterval || requestCounter % 20 === 0) { console.log('📡 UDP REQUEST #' + requestCounter + ' von ' + rinfo.address + ' - Method: ' + (message.includes('EM.GetStatus') ? 'EM.GetStatus' : 'Shelly.GetStatus')); lastLogTime = now; } // Parse die Anfrage um die ID zu bekommen var requestData; try { requestData = JSON.parse(message); } catch (e) { requestData = { id: 1, method: 'EM.GetStatus', params: { id: 0 } }; } // ✅ VERSCHIEDENE RPC RESPONSES je nach Method! var rpcResponse; if (message.includes('EM.GetStatus')) { // ✅ MARSTEK VENUS E UDP REQUEST → NUR 4 FELDER AUF ROOT-EBENE! rpcResponse = createMarstekVenusEResponse(); console.log('🎯 UDP EM.GetStatus! Sende MARSTEK FORMAT: total_act_power=' + rpcResponse.total_act_power + 'W (ROOT-LEVEL)'); } else { // Legacy UDP Request → VERSCHACHTELTE Struktur rpcResponse = { "id": requestData.id || 1, "src": "shellypro3em-a8032ab12345", "result": createShellyGetStatusResponse() }; console.log('📊 UDP Shelly.GetStatus! Sende em:0.total_act_power: ' + powerCalc.marstekPower + 'W (VERSCHACHTELT)'); } var responseStr = JSON.stringify(rpcResponse); socket.send(responseStr, rinfo.port, rinfo.address, function(err) { if (err && MARSTEK_CONFIG.debugMode) { console.log('Fehler beim Senden: ' + err); } }); } // Fallback: JSON RPC versuchen zu parsen else { try { var rpcRequest = JSON.parse(message); if (rpcRequest.method) { console.log('🔧 RPC Method: ' + rpcRequest.method); var rpcResponse = createMarstekVenusEResponse(); var responseStr = JSON.stringify(rpcResponse); socket.send(responseStr, rinfo.port, rinfo.address); console.log('📤 JSON RPC Response gesendet'); } } catch (e) { if (MARSTEK_CONFIG.debugMode) { console.log('❓ Unbekannte UDP-Nachricht: ' + message.substring(0, 100)); } } } } catch (error) { console.log('❌ Fehler bei UDP-Verarbeitung: ' + error); } } // UDP Socket für Discovery erstellen function createUDPSocket() { udpSocket = dgram.createSocket('udp4'); udpSocket.on('message', function(msg, rinfo) { var message = msg.toString(); // Discovery Response if (message.includes('M-SEARCH') || message.includes('shelly') || message.includes('SSDP')) { var localIP = getLocalIP(); var response = 'HTTP/1.1 200 OK\r\n' + 'ST: urn:shelly:device\r\n' + 'USN: uuid:shelly-pro3em-emulation\r\n' + 'LOCATION: http://' + localIP + ':' + EMULATION_PORT + '/settings\r\n' + 'SERVER: Shelly/1.14.0\r\n' + 'CACHE-CONTROL: max-age=1800\r\n' + '\r\n'; udpSocket.send(response, rinfo.port, rinfo.address, function(err) { // Kein Success-Logging für Discovery }); } // Falls doch RPC über Port 1900 kommt else { handleRPCRequest(msg, rinfo, udpSocket); } }); udpSocket.on('error', function(err) { console.log('UDP Discovery Fehler: ' + err); }); } // Emulation starten function startEmulation() { if (serverInstance) { console.log('Emulation läuft bereits'); return; } try { // HTTP Server starten serverInstance = server.listen(EMULATION_PORT, function() { console.log('=== ECHTE SHELLY PRO 3EM EMULATION GESTARTET ==='); console.log('Port: ' + EMULATION_PORT); console.log('Erreichbar unter: http://localhost:' + EMULATION_PORT); console.log(''); console.log('✅ ECHTE API STRUKTUR:'); console.log(' /rpc/EM.GetStatus → DIREKTE Struktur (für Venus E)'); console.log(' /rpc/Shelly.GetStatus → VERSCHACHTELTE Struktur (Legacy)'); console.log(''); console.log('🎯 Venus E sollte /rpc/EM.GetStatus verwenden!'); console.log('📊 Verwendet echte Shelly EM3 Pro Messwerte!'); console.log('========================================================'); }); // UDP Discovery starten (Port 1900) createUDPSocket(); udpSocket.bind(1900, function() { console.log('UDP Discovery läuft auf Port 1900'); }); // UDP Socket für Port 1010 (RPC) var rpcSocket = dgram.createSocket('udp4'); rpcSocket.bind(1010, function() { console.log('UDP RPC Socket läuft auf Port 1010'); }); rpcSocket.on('message', function(msg, rinfo) { handleRPCRequest(msg, rinfo, rpcSocket); }); rpcSocket.on('error', function(err) { console.log('UDP RPC Port 1010 Fehler: ' + err); }); // Regelmäßige Updates der Messwerte setInterval(readShellyValues, UPDATE_INTERVAL); // Initiale Werte laden readShellyValues(); } catch (error) { console.error('Fehler beim Starten der Emulation:'); console.error(error); } } function stopEmulation() { if (serverInstance) { serverInstance.close(function() { console.log('Shelly EM3 Pro Emulation gestoppt'); serverInstance = null; }); } if (udpSocket) { udpSocket.close(function() { console.log('UDP Discovery gestoppt'); }); } } // ZUSÄTZLICHE Überwachungsfunktion für Marstek-Verhalten function monitorMarstekBehavior() { console.log('\n=== MARSTEK STATUS UPDATE ==='); console.log('⚡ Aktueller Verbrauch: ' + currentPowerTotal + 'W'); console.log('📊 Phase Details: L1=' + currentPowerL1.toFixed(1) + 'W, L2=' + currentPowerL2.toFixed(1) + 'W, L3=' + currentPowerL3.toFixed(1) + 'W'); console.log('🔌 Current Details: L1=' + currentCurrentL1.toFixed(2) + 'A, L2=' + currentCurrentL2.toFixed(2) + 'A, L3=' + currentCurrentL3.toFixed(2) + 'A'); console.log('⚡ Voltage Details: L1=' + currentVoltageL1.toFixed(1) + 'V, L2=' + currentVoltageL2.toFixed(1) + 'V, L3=' + currentVoltageL3.toFixed(1) + 'V'); console.log('📡 UDP Requests: ' + requestCounter + ' (seit Start)'); console.log('🔧 API-Struktur: EM.GetStatus (DIREKT) + Shelly.GetStatus (VERSCHACHTELT)'); if (currentPowerTotal !== 0) { var calc = calculateMarstekPower(currentPowerTotal); console.log('🎯 Venus E erhält: total_act_power=' + calc.marstekPower + 'W (' + calc.direction + ')'); } // Reset Counter für bessere Übersicht if (requestCounter > 1000) { requestCounter = 0; console.log('🔄 Request Counter zurückgesetzt'); } console.log('=============================\n'); } // Monitoring alle 2 Minuten setInterval(monitorMarstekBehavior, 120000); // Emulation starten startEmulation(); // Cleanup bei Script-Stop onStop(function() { console.log('Script wird gestoppt...'); stopEmulation(); }); // Test-Funktion für Venus E Kompatibilität function testVenusECompatibility() { console.log('\n=== VENUS E KOMPATIBILITÄTS-TEST ==='); console.log('🎯 Teste EM.GetStatus (Venus E Endpoint):'); var emResponse = createMarstekVenusEResponse(); console.log('- total_act_power: ' + (emResponse.total_act_power !== undefined ? '✅ ' + emResponse.total_act_power + 'W' : '❌ FEHLT')); console.log('- id: ' + (emResponse.id !== undefined ? '✅ ' + emResponse.id : '❌ FEHLT')); console.log('- a_act_power: ' + (emResponse.a_act_power !== undefined ? '✅ ' + emResponse.a_act_power + 'W' : '❌ FEHLT')); console.log('- Struktur: ' + (emResponse.result === undefined ? '✅ ROOT-LEVEL (korrekt für Venus E)' : '❌ VERSCHACHTELT')); console.log('- Felder-Anzahl: ' + (Object.keys(emResponse).length === 4 ? '✅ 4 Felder (korrekt)' : '❌ ' + Object.keys(emResponse).length + ' Felder')); console.log('- Felder: ' + JSON.stringify(Object.keys(emResponse))); console.log('\n📊 Teste Shelly.GetStatus (Legacy Endpoint):'); var shellyResponse = createShellyGetStatusResponse(); console.log('- em:0.total_act_power: ' + (shellyResponse['em:0'] && shellyResponse['em:0'].total_act_power !== undefined ? '✅ ' + shellyResponse['em:0'].total_act_power + 'W' : '❌ FEHLT')); console.log('- Struktur: ' + (shellyResponse['em:0'] !== undefined ? '✅ VERSCHACHTELT (korrekt für Legacy)' : '❌ NICHT VERSCHACHTELT')); console.log('\n📊 ECHTE SHELLY-DATEN INTEGRATION:'); console.log('- Power Werte: ' + (currentPowerL1 !== 0 || currentPowerL2 !== 0 || currentPowerL3 !== 0 ? '✅ Echte Werte geladen' : '❌ Keine echten Werte')); console.log('- Current Werte: ' + (currentCurrentL1 !== 0 || currentCurrentL2 !== 0 || currentCurrentL3 !== 0 ? '✅ Echte Ampere-Werte' : '❌ Standard-Werte')); console.log('- PowerFactor: ' + (currentPowerFactorL1 !== 1.0 || currentPowerFactorL2 !== 1.0 || currentPowerFactorL3 !== 1.0 ? '✅ Echte PF-Werte' : '❌ Standard-Werte')); console.log('- Energie-Werte: ' + (energyL1 > 0 || energyL2 > 0 || energyL3 > 0 ? '✅ Echte Energie-Daten' : '❌ Keine Energie-Daten')); console.log('\n💡 LOGGING KONFIGURATION:'); console.log('- Debug Mode: ' + (MARSTEK_CONFIG.debugMode ? '🔍 AN (ausführlich)' : '🔇 AUS (reduziert)')); console.log('- Change Threshold: ' + MARSTEK_CONFIG.minChangeThreshold + 'W (nur bei größeren Änderungen loggen)'); console.log('- Log Interval: ' + (MARSTEK_CONFIG.logInterval / 1000) + 's (reduziert Spam)'); console.log('====================================\n'); } // Test bei Start ausführen setTimeout(testVenusECompatibility, 3000); // DEBUGGING: Zeige alle HTTP Requests im Detail function enableDetailedLogging() { MARSTEK_CONFIG.debugMode = true; MARSTEK_CONFIG.minChangeThreshold = 0; // Jede Änderung loggen MARSTEK_CONFIG.logInterval = 5000; // Alle 5 Sekunden console.log('🔍 DETAILED LOGGING AKTIVIERT'); console.log('- Jede Wert-Änderung wird geloggt'); console.log('- Alle HTTP/UDP Requests werden geloggt'); console.log('- Echte Shelly-Werte werden im Detail angezeigt'); console.log('- Aufruf: disableDetailedLogging() zum Deaktivieren'); } function disableDetailedLogging() { MARSTEK_CONFIG.debugMode = false; MARSTEK_CONFIG.minChangeThreshold = 50; // Nur große Änderungen MARSTEK_CONFIG.logInterval = 30000; // Alle 30 Sekunden console.log('🔇 DETAILED LOGGING DEAKTIVIERT'); console.log('- Nur noch wichtige Änderungen werden geloggt'); console.log('- Reduzierte Log-Ausgabe für bessere Übersicht'); } // Spezielle Test-Funktion für echte Shelly-Werte function testRealShellyValues() { console.log('\n=== ECHTE SHELLY-WERTE TEST ==='); console.log('📊 Teste Shelly EM3 Instance: ' + SHELLY_EM3_INSTANCE); try { // Test alle Shelly-Werte var shellyL1Power = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Power').val; var shellyL1Current = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Current').val; var shellyL1PF = getState(SHELLY_EM3_INSTANCE + '.Emeter0.PowerFactor').val; var shellyL1Energy = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total').val; var shellyL1Returned = getState(SHELLY_EM3_INSTANCE + '.Emeter0.Total_Returned').val; console.log('Phase A (L1):'); console.log(' Power: ' + (shellyL1Power !== null ? shellyL1Power + 'W ✅' : 'FEHLT ❌')); console.log(' Current: ' + (shellyL1Current !== null ? shellyL1Current + 'A ✅' : 'FEHLT ❌')); console.log(' PowerFactor: ' + (shellyL1PF !== null ? shellyL1PF + ' ✅' : 'FEHLT ❌')); console.log(' Energy: ' + (shellyL1Energy !== null ? shellyL1Energy + 'Wh ✅' : 'FEHLT ❌')); console.log(' Returned: ' + (shellyL1Returned !== null ? shellyL1Returned + 'Wh ✅' : 'FEHLT ❌')); // Tibber Test var tibberPower = getState('tibberlink.0.LocalPulse.0.Power').val; console.log('\nTibber Pulse:'); console.log(' Power: ' + (tibberPower !== null ? tibberPower + 'W ✅' : 'FEHLT ❌')); } catch (error) { console.log('❌ Fehler beim Testen der Shelly-Werte: ' + error); } console.log('====================================\n'); } // Test der echten Werte bei Start setTimeout(testRealShellyValues, 5000); // Aufruf: enableDetailedLogging() zum Aktivieren des Detailed Logging // Aufruf: testRealShellyValues() zum Testen der Shelly-Verbindung