logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Skrypt do sterowania roletami elektrycznymi przy użyciu wyjścia fotowoltaicznego jako wejścia czujni

io2345 04 Maj 2025 12:15 552 4
Treść została przetłumaczona angielski » polski Zobacz oryginalną wersję tematu
  • #1 21538532
    io2345
    Poziom 9  
    Posty: 268
    Pomógł: 1
    Ocena: 7
    Muszę zastąpić czujnik światła, który był używany do zamykania rolet elektrycznych w słoneczne dni, aby zapobiec przegrzaniu pomieszczeń.
    Szukałem na WWW czujników światła Tuya, ale nic nie brzmiało zbyt obiecująco - albo komentarze były złe, albo mogły mierzyć tylko słabe światło, ale nie światło słoneczne.

    Teraz mam inny pomysł na podejście: Mam kilka paneli słonecznych działających w systemie PV. I tak radzą sobie ze światłem. Wszystko, czego potrzebuję, to sprytny skrypt, który wykrywa odpowiednie warunki otwarcia i zamknięcia: Na przykład, jeśli produkcja energii słonecznej przekracza 50% wartości maksymalnej, rolety zamykają się. Jeśli spadnie o 25% i utrzyma się na tym poziomie przez kilka minut (np. 10), rolety podniosą się. Jeśli spadnie znacznie w krótszym czasie (np. 50% w ciągu minuty), rolety podniosą się jeszcze szybciej, ponieważ może to oznaczać, że zbliża się burza.

    Czy ktoś ma już działający skrypt do tego celu, który jest niezawodny?
  • #2 21539900
    xury
    Specjalista automatyka domowa
    Posty: 7076
    Pomógł: 877
    Ocena: 1490
    Raczej nikt gotowego skryptu nie ma. Nie wiadomo nawet skąd miałby być pobierane dane do niego.
    Można wykorzystać na przykład czujnik BH1750 i Tasmota.
    A skrypt jeśli dobrze opiszesz wygeneruje Ci Chatgpt lub Deepseek.
  • #3 21540581
    io2345
    Poziom 9  
    Posty: 268
    Pomógł: 1
    Ocena: 7
    >>21539900 Dziękuję za odpowiedź. Oczywiście mógłbym wprowadzić kolejny inteligentny element - ale po co, skoro jest już niezawodny, wodoodporny, długowieczny "czujnik", który już dostarcza dane do systemu inteligentnego domu?

    Hinzugefügt nach 1 [Stunden] 57 [Minuten]:

    Podzielę się z wami moimi przemyśleniami: Oryginalny czujnik, którego używałem, był bezprzewodowo podłączony do trzech zastrzeżonych "inteligentnych" sterowników żaluzji. Pierwszy padł po trzech latach, następny wytrzymał cztery lata. Każdy z nich kosztował około 50 €.
    Co robił czujnik? Miał bardzo mały panel słoneczny z przodu (skierowany w stronę okna) i mierzył napięcie panelu. Jeśli przez 10 minut napięcie przekraczało regulowany poziom, rolety opuszczały się. To wszystko. 100 euro za dwa czujniki i 100 euro za każde sterowanie roletami, więc 400 euro za cały system. Dużo pieniędzy.
    Postanowiłem więc zastąpić cały system tańszym rozwiązaniem. Sterowniki żaluzji Tuya kosztują około 5€ za sztukę. Zamówiłem już trzy z nich za 4,29€ każdy. OK, potrzebuję również przełączników ręcznych, które pasują do serii urządzeń elektrycznych. Kosztują 15€ za sztukę. W sumie nawet nie 20€ za sterownik.
    Następnie pomyślałem o zamienniku czujnika. Moim pierwszym pomysłem było skopiowanie tego, co robił oryginał: Mierzenie napięcia małego panelu słonecznego i przesyłanie go przez ESP lub coś podobnego do systemu Smart Home (ioBroker w moim przypadku). Ale potrzebowałbym przynajmniej małej obudowy i źródła zasilania. Wadą czujnika przymocowanego do okna jest to, że nigdy nie można całkowicie zamknąć rolet na tym oknie, ponieważ wtedy czujnik nie działałby już zgodnie z przeznaczeniem lub nie otwierałby rolet ponownie.
    I wtedy w końcu wpadłem na pomysł, że na dachu są już panele słoneczne, które co 20 sekund przesyłają swoją wartość mocy do systemu inteligentnego domu, więc dlaczego by ich nie wykorzystać.
    To rozwiązanie jest nawet lepsze, ponieważ można wziąć pod uwagę stan termowłącznika w pokoju (tego używanego do ogrzewania podłogowego. Ich status pobieram z tego projektu: Link ). Jeśli w pomieszczeniu jest zimno i dlatego włączony jest termowłącznik, pozostaw żaluzje podniesione w słoneczny dzień i pozwól słońcu ogrzać pomieszczenie. :-)
  • #4 21546117
    io2345
    Poziom 9  
    Posty: 268
    Pomógł: 1
    Ocena: 7
    Oto skrypt, którego obecnie używam (w ioBroker). Zamyka i otwiera rolety w zależności od produkcji energii słonecznej, temperatury zewnętrznej i stanu termostatu pokojowego. Skrypt jest nieaktywny poniżej danej temperatury zewnętrznej. Jeśli produkcja energii słonecznej (jako wskaźnik mocy światła słonecznego) jest wystarczająco wysoka, zamknie rolety, chyba że ogrzewanie pomieszczenia (stan termostatu) jest wyłączone. Jeśli termostat pokojowy jest włączony, a tym samym włączone jest ogrzewanie, rolety podniosą się, aby słońce pomogło ogrzać pomieszczenie. Dodatkowo polecenia góra/dół są blokowane na kilka minut po otwarciu/zamknięciu rolet, aby uniknąć ciągłych cykli góra/dół w pochmurne dni.


    // === Setze den tatsächlichen Datenpunkt ===
    const PV_INPUT_DP = 'javascript.0.Geräte.Photovoltaik.Leistung_aktuell';  // Der tatsächliche Datenpunkt für die PV-Leistung
    const PV_AVG_DP = '0_userdata.0.EigeneDatenpunkte.Wertesammler.PV-Mittelwert_Hausdach'; // Mittelwert-Datenpunkt
    const TEMP_SENSOR_DP = 'mqtt.0.Vito.WP.Außentemperatur'; // Außentemperatur-Datenpunkt
    
    // === Jalousien und deren Datenpunkte ===
    const jalousien = [
        {
            name: 'KA', // Name der Jalousie
            dpUp: 'mqtt.0.Jalousie_KA.6.set', // Datenpunkt für das Hochfahren der Jalousie
            dpDown: 'mqtt.0.Jalousie_KA.1.set', // Datenpunkt für das Runterfahren der Jalousie
            thermo: 'mqtt.0.6-channel-relay.13.get' // Datenpunkt des zugehörigen Raumthermostats
        },
        {
            name: 'KI',
            dpUp: 'mqtt.0.Jalousie_KI.6.set',
            dpDown: 'mqtt.0.Jalousie_KI.1.set',
            thermo: 'mqtt.0.6-channel-relay.15.get'
        },
        {
            name: 'WO',
            dpUp: 'mqtt.0.Jalousie_WO.6.set',
            dpDown: 'mqtt.0.Jalousie_WO.1.set',
            thermo: 'mqtt.0.6-channel-relay.14.get'
        }
    ];
    
    // === Zeit- und Sperrzeiten ===
    const LOCK_TIME = 8 * 60 * 1000; // Sperrzeit für Jalousien, wenn sie vom Script geschlossen wurden (8 Minuten)
    const JALOUSIE_DURATION = 28 * 1000; // Dauer des Schließens/Öffnens (30 Sekunden)
    const JALOUSIE_OPEN_DURATION = 30 * 1000; // Öffnungsdauer der Jalousie
    const OPEN_LOCK_TIME = 2 * 60 * 1000; // Sperrzeit nach Öffnen der Jalousie
    const TEMP_LIMIT = 9; // Grenzwert der Außentemperatur, unterhalb dessen wird die Jalousie wieder geöffnet
    const PV_LOW_THRESHOLD = 825; // Schwellenwert für den Mittelwert der PV-Leistung, unterhalb dessen die Jalousie schneller öffnen muss
    const PV_HIGH_THRESHOLD = 2800; // Schwellenwert für den Mittelwert der PV-Leistung, über dem die Jalousie geschlossen werden soll
    
    // === Datenpunkte für die Speicherung ===
    let jalousieLock = {}; // Speichert die Sperrzeit für jede Jalousie
    let lastCloseTime = {}; // Speichert den Zeitpunkt des letzten Schließens jeder Jalousie
    let lastChangeTime = {}; // Speichert die Zeit der letzten Änderung des Mittelwerts
    let openLock = {}; // Sperrzeit nach Öffnen der Jalousie
    let jalousieStatus = {}; // Speichert den aktuellen Status der Jalousie (offen/geschlossen)
    
    let lastAvgPV = null;  // Speichert den letzten Mittelwert der PV-Anlage
    let lastAvgPVTime = null;  // Speichert die Zeit der letzten Mittelwert-Berechnung
    
    // === Funktion zur Berechnung des Mittelwerts ===
    let pvValues = [];  // Array zum Speichern der letzten 7 Messwerte
    
    function calculateAvgPV() {
        const pvValue = getState(PV_INPUT_DP).val; // Abrufen der aktuellen PV-Leistung
        
        // Überprüfen, ob der PV-Wert gültig ist (nicht null oder NaN)
        if (pvValue !== null && !isNaN(pvValue)) {
            pvValues.push(pvValue); // Aktuellen Wert in das Array einfügen
        }
    
        // Nur die letzten 7 Werte berücksichtigen
        if (pvValues.length > 7) {
            pvValues.shift(); // Ältesten Wert entfernen, wenn mehr als 7 Werte vorhanden sind
        }
    
        // Berechnung des Mittelwerts
        const sum = pvValues.reduce((acc, val) => acc + val, 0); // Summe aller Werte
        const avgPV = pvValues.length > 0 ? sum / pvValues.length : 0; // Mittelwert berechnen
    
        // Den berechneten Mittelwert auf die nächsten 2 Nachkommastellen runden
        return Math.round(avgPV);
    }
    
    // === Funktion zur Überprüfung der Jalousien-Logik ===
    function checkJalousien(avgPV) {
        // Außentemperatur prüfen
        const currentTemp = getState(TEMP_SENSOR_DP).val;
        
        // Wenn Außentemperatur unter TEMP_LIMIT, keine Jalousien schließen
        if (currentTemp < TEMP_LIMIT) {
            //console.log(`Außentemperatur ist unter ${TEMP_LIMIT}°C. Jalousien werden nicht geschlossen.`);
            return; // Verhindert, dass die Jalousien geschlossen werden
        }
    
        //console.log(`Aktueller Mittelwert der PV-Leistung: ${avgPV} Watt`);
    
        jalousien.forEach(j => {
            const thermo = getState(j.thermo).val;
            const nowTime = new Date().getTime(); // Aktuelle Zeit
    
            // Zustandsüberprüfung für die Sperrzeiten und letzten Änderungen
            const locked = nowTime - jalousieLock[j.name] < LOCK_TIME;
            const isClosed = jalousieStatus[j.name] === 'geschlossen';
            const isOpen = jalousieStatus[j.name] === 'offen';
            const openLocked = nowTime - openLock[j.name] < OPEN_LOCK_TIME; // Sperrzeit nach Öffnen
            const lastCloseTimeDiff = nowTime - (lastCloseTime[j.name] || 0); // Zeit seit dem letzten Schließen
    
            // **Neue Logik für das schnelle Öffnen bei schnellen Änderungen des Mittelwerts**
            // Wenn der Mittelwert innerhalb von 2 Minuten von >2800 auf <825 gesenkt wurde, sofort öffnen
            if (lastAvgPV !== null && lastAvgPV > PV_HIGH_THRESHOLD && avgPV < PV_LOW_THRESHOLD && lastAvgPVTime && (nowTime - lastAvgPVTime <= 2 * 60 * 1000)) {
                setTimeout(() => {
                    setState(j.dpUp, 1);  // Hier ändern wir den Wert von true auf 1
                    setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);  // Nach der Öffnungszeit den Wert auf 0 setzen
                }, 0);
                jalousieStatus[j.name] = 'offen'; // Jalousie-Status auf "offen" setzen
                openLock[j.name] = nowTime; // Sperrzeit setzen
                log(`[${j.name}] Mittelwert fiel innerhalb von 2 Minuten von > 2800 auf < 825, Jalousie wird sofort geöffnet.`);
            }
    
            // Jalousie schließen, wenn Mittelwert > 2800 und Thermostatstatus = 0
            if (avgPV >= PV_HIGH_THRESHOLD && thermo === 0 && !isClosed && !locked && !openLocked) {
                setTimeout(() => {
                    setState(j.dpDown, 1);  // Wert für das Schließen auf 1 setzen
                    setTimeout(() => setState(j.dpDown, 0), JALOUSIE_DURATION);  // Nach der Schließzeit den Wert auf 0 setzen
                }, 0);
                jalousieStatus[j.name] = 'geschlossen'; // Jalousie-Status auf "geschlossen" setzen
                lastCloseTime[j.name] = nowTime; // Speichern des Schließzeitpunkts
                jalousieLock[j.name] = nowTime;
                console.log(`[${j.name}] Jalousie wird geschlossen.`);
            }
    
            // Jalousie öffnen, wenn Mittelwert < 2800 und mehr als 8 Minuten seit dem Schließen vergangen sind
            if (avgPV < PV_HIGH_THRESHOLD && lastCloseTimeDiff >= LOCK_TIME && isClosed && !openLocked) {
                setTimeout(() => {
                    setState(j.dpUp, 1);  // Wert für das Öffnen auf 1 setzen
                    setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);  // Nach der Öffnungszeit den Wert auf 0 setzen
                }, 0);
                jalousieStatus[j.name] = 'offen'; // Jalousie-Status auf "offen" setzen
                openLock[j.name] = nowTime; // Sperrzeit setzen
                console.log(`[${j.name}] Jalousie wird geöffnet (Mittelwert < 2800 und mehr als 8 Minuten seit dem Schließen).`);
            }
    
            // Wenn Thermostat auf 1 geht, Jalousie sofort öffnen, wenn Mittelwert > 3025
            if (thermo === 1 && !isOpen && avgPV > PV_HIGH_THRESHOLD && !openLocked) {
                setTimeout(() => {
                    setState(j.dpUp, 1);  // Wert für das Öffnen auf 1 setzen
                    setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);  // Nach der Öffnungszeit den Wert auf 0 setzen
                }, 0);
                jalousieStatus[j.name] = 'offen'; // Jalousie-Status auf "offen" setzen
                openLock[j.name] = nowTime; // Sperrzeit setzen
                console.log(`[${j.name}] Thermostat auf 1, Jalousie wird sofort geöffnet (Mittelwert > 2800).`);
            }
        });
    
        // Den letzten Mittelwert und die Zeit speichern
        lastAvgPV = avgPV;
        lastAvgPVTime = new Date().getTime();
    }
    
    // === Funktionsaufruf ===
    on({id: PV_INPUT_DP, change: 'ne'}, async function (obj) {
        const avgPV = calculateAvgPV();
        setState(PV_AVG_DP, avgPV); // Den berechneten Mittelwert setzen
        lastChangeTime = new Date().getTime(); // Letzte Änderung des Mittelwerts speichern
        checkJalousien(avgPV); // Jalousien nach der Mittelwertberechnung überprüfen
    });
  • #5 21560851
    io2345
    Poziom 9  
    Posty: 268
    Pomógł: 1
    Ocena: 7
    Najnowsza wersja, która wykrywa również ręczne naciśnięcie przycisku przełącznika ściennego (używam tanich standardowych przełączników mechanicznych podłączonych do małego kontrolera stąd: Link ) i dezaktywuje sterowanie skryptem konkretnej rolety indywidualnie na określony czas. Zawiera również punkt danych dla przełącznika Override (może być używany np. do czyszczenia okien). Wyklucza otwieranie/zamykanie rolet w pomieszczeniach sypialnych rano przed określoną godziną, aby nie obudzić użytkownika. Posiada również mechanizm awaryjnego otwierania rolet, jeśli wartość PV spada bardzo szybko w krótkim czasie (co może oznaczać, że zbliża się front burzowy). Wprowadzono również kilka mniejszych usprawnień, takich jak lepsza ochrona przed ciągłym przesuwaniem żaluzji w górę i w dół.

    // Dieses Script steuert die Jalousien in Abhängigkeit von Sonneneinstrahlung (ermittelt aus
    // der PV-Leistung), Außentemperatur (Script bei niedriger Temperatur inaktiv) sowie dem Status
    // von Raumthermostaten (bei Sonne und laufender Heizung Jalousien auf). Es enthält mehrere Locks,
    // die ein zu häufiges, aufeinanderfolgendes Öffnen/Schließen verhindern (jalousieLock und openLock). 
    // Es enthält einen Datenpunkt, über den es zentral deaktiviert werden kann (Override). Nach
    // manueller Betätigung ist das Script für diese Jalousie blockiert. Die Blockade wird automatisch
    // täglich um 11 Uhr und 21 Uhr deaktiviert. Die Funktion "Schnellöffnung bei Gewitter" funktioniert
    // aber immer.
    
    // === Grundkonfiguration: Datenpunkte für Sensoren und Steuerung ===
    const PV_INPUT_DP = 'javascript.0.Geräte.Photovoltaik.Leistung_aktuell';  // Der Datenpunkt (DP) der den PV-Wert liefert
    const PV_AVG_DP = '0_userdata.0.EigeneDatenpunkte.Wertesammler.PV-Mittelwert_Hausdach'; // DP muss angelegt werden
    const PV_AVG_LONG_DP = '0_userdata.0.EigeneDatenpunkte.Wertesammler.PV-Mittelwert_Hausdach_lang'; // DP muss angelegt werden
    const TEMP_SENSOR_DP = 'mqtt.0.Vito.WP.Außentemperatur'; // Der DP der die Außentemperatur liefert
    const CENTRAL_OVERRIDE = '0_userdata.0.Jalousiescript_aktiviert'; // DP muss angelegt werden. Ist er FALSE, ist die Scriptsteuerung weitgehend deaktiviert
    
    // === Konfiguration der Jalousien (Up/Down/Status) ===
    const jalousien = [
       { name: 'WO', dpUp: 'mqtt.0.Jalousie_WO.6.set', dpDown: 'mqtt.0.Jalousie_WO.1.set', thermo: 'mqtt.0.HK_Relais_DG.14.get' }, //DPs der Jalousiesteuerung und der Thermostate
       { name: 'KI', dpUp: 'mqtt.0.Jalousie_KI.6.set', dpDown: 'mqtt.0.Jalousie_KI.1.set', thermo: 'mqtt.0.HK_Relais_DG.15.get' },
       { name: 'KA', dpUp: 'mqtt.0.Jalousie_KA.6.set', dpDown: 'mqtt.0.Jalousie_KA.1.set', thermo: 'mqtt.0.HK_Relais_DG.13.get' }
    ];
    
    // === Zeit- und Schwellenwerte ===
    const LOCK_TIME = 12 * 60 * 1000;  // So lange bleibt die Jalousie nach Schließen mindestens zu, z.B. 12 Minuten
    const OPEN_LOCK_TIME = 5 * 60 * 1000; // So lange bleibt die Jalousie nach Öffnen mindestens auf, z.B. 5 Minuten
    const JALOUSIE_DURATION = 31 * 1000;  //Laufzeit der Jalousie zum Schließen, z.B. 31 Sekunden
    const JALOUSIE_OPEN_DURATION = 34 * 1000; //Laufzeit der Jalousie zum Öffnen, z.B. 34 Sekunden
    const TEMP_LIMIT = 9; //Unterhalb dieser Temperatur in °C ist das Script weitgehend inaktiv
    const PV_LOW_THRESHOLD = 700; // Wert in Watt, auf die der PV-Wert in kurzer Zeit fallen muss für Schnellöffnung
    const PV_HIGH_THRESHOLD = 2400; // Wert in Watt, bei der die Jalousien schließen
    const PV_OPEN_THRESHOLD = PV_HIGH_THRESHOLD * 0.9; // Prozentwert, bei dem die Jalousien öffnen
    const PV_LONG_TERM_VALUES = 30; // Anzahl der gelieferten PV-Werte, mit der der PV-Durchschnittswert Langzeit gebildet wird
    const SLEEPROOM_ACTIVE_HOUR = 11; // Uhrzeit, bis zu der die Schlafräume von Scriptaktionen ausgenommen sind
    const SCRIPT_TRIGGER_WINDOW = 15000;  
    
    // === Variablen zur Statusverfolgung ===
    let jalousieLock = {};
    let lastCloseTime = {};
    let openLock = {};
    let jalousieStatus = {};
    let scriptTriggered = {};
    let manualLockLogged = {};
    let pvValues = [];
    let pvLongTermValues = [];
    let lastAvgPV = null;
    let lastAvgPVTime = null;
    let fastOpenByPV = {};
    let fastOpenTime = {};
    
    // === Mittelwertberechnung ===
    function calculateMovingAverage(arr, maxLength) {
        if (arr.length > maxLength) arr.shift();
        const sum = arr.reduce((a, b) => a + b, 0);
        return Math.round(arr.length ? sum / arr.length : 0);
    }
    
    jalousien.forEach(j => {
        if (!jalousieStatus[j.name]) {
            jalousieStatus[j.name] = 'geschlossen';
        }
    });
    
    // === Statusdatenpunkt schreiben ===
    function updateRolloStatus(name, percent) {
        setState(`0_userdata.0.Rollo_${name}`, percent, true);
    }
    
    function markScriptTrigger(name) {
        scriptTriggered[name] = true;
        setTimeout(() => { scriptTriggered[name] = false; }, SCRIPT_TRIGGER_WINDOW);
    }
    
    // === Hauptlogik zur Steuerung der Jalousien ===
    function checkJalousien(avgPVLong, avgPVShort) {
        const now = Date.now();
        const hour = new Date().getHours();
    
        const fastOpenPossible = (
            lastAvgPV !== null &&
            lastAvgPV > PV_HIGH_THRESHOLD &&
            avgPVShort < PV_LOW_THRESHOLD &&
            lastAvgPVTime && now - lastAvgPVTime <= 3 * 60 * 1000
        );
    
       // === Schnellöffnung bei PV-Abfall ===
        if (fastOpenPossible) {
            log('Schnellöffnung aufgrund PV-Abfall innerhalb 3 Minuten aktiviert.');
            jalousien.forEach(j => {
                if (jalousieStatus[j.name] !== 'offen') {
                    openLock[j.name] = 0;
                    jalousieLock[j.name] = 0;
    
                    log(`[${j.name}] Schnellöffnung: Jalousie wird geöffnet (Lock zurückgesetzt).`);
    
                    updateRolloStatus(j.name, 0);
                    setState(j.dpUp, 1);
                    setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
    
                    jalousieStatus[j.name] = 'offen';
                    openLock[j.name] = now;
                    fastOpenByPV[j.name] = true;
                    fastOpenTime[j.name] = now;
    
                    markScriptTrigger(j.name);
                } else {
                    log(`[${j.name}] Schnellöffnung: Jalousie ist bereits offen.`);
                }
            });
            return;
        }
    
        const currentTemp = getState(TEMP_SENSOR_DP).val;
        if (currentTemp < TEMP_LIMIT) {
            log(`Temperatur unter TEMP_LIMIT (${TEMP_LIMIT} °C). Hauptlogik wird nicht ausgeführt.`);
            return;
        }
    
        if (!getState(CENTRAL_OVERRIDE).val) {
            log('Zentral-Override aktiv – Automatik deaktiviert.');
            return;
        }
    
     // === PV-basierte Logik ===
        jalousien.forEach(j => {
            const disableDP = `0_userdata.0.JalousieDisable.${j.name}`;
            const disabled = getState(disableDP)?.val === true;
            const isClosed = jalousieStatus[j.name] === 'geschlossen';
            const isOpen = jalousieStatus[j.name] === 'offen';
            const locked = now - (jalousieLock[j.name] || 0) < LOCK_TIME;
            const openLocked = now - (openLock[j.name] || 0) < OPEN_LOCK_TIME;
            const thermo = getState(j.thermo).val;
    
            if (disabled) {
        if (!manualLockLogged[j.name]) {
            log(`[${j.name}] Automatik deaktiviert durch manuelle Bedienung.`);
            manualLockLogged[j.name] = true;
        }
        return;
        } else {
        if (manualLockLogged[j.name]) {
            log(`[${j.name}] Automatik wieder aktiviert.`);
            manualLockLogged[j.name] = false;
        }
        }
    
            if ((j.name === 'KA' || j.name === 'KI') && hour < SLEEPROOM_ACTIVE_HOUR) {
                log(`[${j.name}] Schlafraum – Automatik gesperrt bis ${SLEEPROOM_ACTIVE_HOUR} Uhr.`);
                return;
            }
    
            if (currentTemp < TEMP_LIMIT && isClosed && !openLocked) {
                updateRolloStatus(j.name, 0);
                markScriptTrigger(j.name);
                setState(j.dpUp, 1);
                setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
                jalousieStatus[j.name] = 'offen';
                openLock[j.name] = now;
                log(`[${j.name}] Temperatur < ${TEMP_LIMIT} °C – Jalousie geöffnet.`);
                return;
            }
            // === Schließen bei ausreichend PV ===
    
            if (avgPVLong >= PV_HIGH_THRESHOLD && thermo === 0 && !isClosed && !locked && !openLocked) {
                log(`[${j.name}] PV hoch (${avgPVLong}) und Thermostat inaktiv, Jalousie wird geschlossen.`);
                updateRolloStatus(j.name, 100);
                markScriptTrigger(j.name);
                setState(j.dpDown, 1);
                setTimeout(() => setState(j.dpDown, 0), JALOUSIE_DURATION);
                jalousieStatus[j.name] = 'geschlossen';
                jalousieLock[j.name] = now;
                lastCloseTime[j.name] = now;
                return;
            } else {
                if (locked) log(`[${j.name}] Lock aktiv – kein Schließen.`);
                if (openLocked) log(`[${j.name}] OpenLock aktiv – kein Schließen.`);
            }
    
            // === Öffnen bei PV-Rückgang (nur wenn PV_OPEN_THRESHOLD unterschritten) ===
            if (avgPVLong < PV_OPEN_THRESHOLD && isClosed && !openLocked &&
                (!lastCloseTime[j.name] || now - lastCloseTime[j.name] >= LOCK_TIME)) {
                log(`[${j.name}] PV Rückgang erkannt (${avgPVLong} < ${PV_OPEN_THRESHOLD}), Jalousie wird geöffnet.`);
                updateRolloStatus(j.name, 0);
                markScriptTrigger(j.name);
                setState(j.dpUp, 1);
                setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
                jalousieStatus[j.name] = 'offen';
                openLock[j.name] = now;
                return;
            }
            // === Öffnung bei aktivem Thermostat ===
            if (thermo === 1 && !isOpen && avgPVLong > PV_HIGH_THRESHOLD && !openLocked) {
                log(`[${j.name}] Thermostat aktiv & PV hoch (${avgPVLong}), Jalousie wird geöffnet.`);
                updateRolloStatus(j.name, 0);
                markScriptTrigger(j.name);
                setState(j.dpUp, 1);
                setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
                jalousieStatus[j.name] = 'offen';
                openLock[j.name] = now;
                return;
            }
        });
    
       // log('--- checkJalousien beendet ---');
    }
    
    // === Reaktion auf neue PV-Daten (Trigger) ===
    on({ id: PV_INPUT_DP, change: 'ne' }, () => {
        const val = getState(PV_INPUT_DP).val;
        if (val != null && !isNaN(val)) {
            pvValues.push(val);
            pvLongTermValues.push(val);
        }
    
        const avgShort = calculateMovingAverage(pvValues, 7);
        const avgLong = calculateMovingAverage(pvLongTermValues, PV_LONG_TERM_VALUES);
        setState(PV_AVG_DP, avgShort);
        setState(PV_AVG_LONG_DP, avgLong);
    
        if (avgShort > PV_HIGH_THRESHOLD) {
            lastAvgPV = avgShort;
            lastAvgPVTime = Date.now();
        }
    
        checkJalousien(avgLong, avgShort);
    });
    
    // === Überwachung manueller Steuerung über Taster ===
    jalousien.forEach(j => {
        const dpUpGet = j.dpUp.replace('.set', '.get');
        const dpDownGet = j.dpDown.replace('.set', '.get');
        let upStart = null;
        let downStart = null;
    
        function handleManualTrigger(name) {
            if (!scriptTriggered[name]) {
                const disableDP = `0_userdata.0.JalousieDisable.${name}`;
                setState(disableDP, true);
                log(`[${name}] Manuelle Bedienung erkannt – Automatik deaktiviert bis 11 Uhr oder 21 Uhr.`);
            }
        }
    
        on({ id: dpUpGet, change: 'ne' }, obj => {
        if (obj.state.val === 1) {
            if (!scriptTriggered[j.name]) {
                handleManualTrigger(j.name);
            } else {
                log(`[${j.name}] Trigger durch Script erkannt – kein manueller Eingriff (UP).`);
            }
            upStart = Date.now();
        } else if (obj.state.val === 0 && upStart) {
            const duration = Date.now() - upStart;
            const percent = Math.max(0, Math.min(100, 100 - Math.round((duration / JALOUSIE_OPEN_DURATION) * 100)));
            updateRolloStatus(j.name, percent);
            upStart = null;
            log(`[${j.name}] Manuelle Öffnung Dauer: ${duration}ms, Status: ${percent}%`);
        }
    });
    
    on({ id: dpDownGet, change: 'ne' }, obj => {
        if (obj.state.val === 1) {
            if (!scriptTriggered[j.name]) {
                handleManualTrigger(j.name);
            } else {
                log(`[${j.name}] Trigger durch Script erkannt – kein manueller Eingriff (DOWN).`);
            }
            downStart = Date.now();
        } else if (obj.state.val === 0 && downStart) {
            const duration = Date.now() - downStart;
            const percent = Math.max(0, Math.min(100, Math.round((duration / JALOUSIE_DURATION) * 100)));
            updateRolloStatus(j.name, percent);
            downStart = null;
            log(`[${j.name}] Manuelle Schließung Dauer: ${duration}ms, Status: ${percent}%`);
        }
    });
    
    });
    
    // === Locks nach manueller Bedienung zurücksetzen ===
    schedule('0 11 * * *', () => {
        jalousien.forEach(j => {
            setState(`0_userdata.0.JalousieDisable.${j.name}`, false);
            log(`[${j.name}] Automatik-Disable zurückgesetzt um 11 Uhr.`);
        });
    });
    
    schedule('0 21 * * *', () => {
        jalousien.forEach(j => {
            setState(`0_userdata.0.JalousieDisable.${j.name}`, false);
            log(`[${j.name}] Automatik-Disable zurückgesetzt um 21 Uhr.`);
        });
    });
    
    log('Jalousiescript gestartet. Automatik aktiv.');
REKLAMA