>>21546117 I had some things added, like full open/close on long button press, manual override switch, collision protection between manual and script based actions, or different PV limits for opening and closeing of blinds.
Here is the latest version. If you have additional ideas what could be improved, let me know:
----
Skasowano z tematu: Script to Control Electric Blinds Using PV Solar Output as Sunlight Sensor Input
przez io2345 dnia 23 Mai 2025 10:26
Here is the latest version. If you have additional ideas what could be improved, let me know:
// === Grundkonfiguration: Datenpunkte für Sensoren und Steuerung ===
const PV_INPUT_DP = 'javascript.0.Geräte.Photovoltaik.Leistung_aktuell';
const PV_AVG_DP = '0_userdata.0.EigeneDatenpunkte.Wertesammler.PV-Mittelwert_Hausdach';
const PV_AVG_LONG_DP = '0_userdata.0.EigeneDatenpunkte.Wertesammler.PV-Mittelwert_Hausdach_lang';
const TEMP_SENSOR_DP = 'mqtt.0.Vito.WP.Außentemperatur';
const CENTRAL_OVERRIDE = '0_userdata.0.Jalousiescript_aktiviert'; // true = Script aktiv
// === Konfiguration der Jalousien (Up/Down/Status) ===
const jalousien = [
{
name: 'KA',
dpUp: 'mqtt.0.Jalousie_KA.6.set',
dpDown: 'mqtt.0.Jalousie_KA.1.set',
thermo: 'mqtt.0.HK_Relais_DG.13.get',
statusDp: '0_userdata.0.Rollo_KA'
},
{
name: 'KI',
dpUp: 'mqtt.0.Jalousie_KI.6.set',
dpDown: 'mqtt.0.Jalousie_KI.1.set',
thermo: 'mqtt.0.HK_Relais_DG.15.get',
statusDp: '0_userdata.0.Rollo_KI'
},
{
name: 'WO',
dpUp: 'mqtt.0.Jalousie_WO.6.set',
dpDown: 'mqtt.0.Jalousie_WO.1.set',
thermo: 'mqtt.0.HK_Relais_DG.14.get',
statusDp: '0_userdata.0.Rollo_WO'
}
];
// === Zeit- und Schwellenwerte ===
const LOCK_TIME = 10 * 60 * 1000;
const JALOUSIE_DURATION = 31 * 1000;
const JALOUSIE_OPEN_DURATION = 34 * 1000;
const OPEN_LOCK_TIME = 4 * 60 * 1000;
const TEMP_LIMIT = 9;
const PV_LOW_THRESHOLD = 700;
const PV_HIGH_THRESHOLD = 2400;
const PV_OPEN_THRESHOLD = PV_HIGH_THRESHOLD * 0.9;
const PV_LONG_TERM_VALUES = 30;
const SLEEPROOM_ACTIVE_HOUR = 11;
// === Variablen zur Statusverfolgung ===
let jalousieLock = {};
let lastCloseTime = {};
let lastChangeTime = {};
let openLock = {};
let jalousieStatus = {};
let lastAvgPV = null;
let lastAvgPVTime = null;
let manualLock = {};
const MANUAL_LOCK_DURATION = JALOUSIE_OPEN_DURATION;
// === Arrays zur Mittelwertberechnung ===
let pvValues = [];
let pvLongTermValues = [];
// === Mittelwertberechnung
function calculateMovingAverage(arr, maxLength) {
if (arr.length > maxLength) arr.shift();
const sum = arr.reduce((acc, val) => acc + val, 0);
return Math.round(arr.length > 0 ? sum / arr.length : 0);
}
// === Statusdatenpunkt schreiben
function updateRolloStatus(name, percent) {
const dp = `0_userdata.0.Rollo_${name}`;
setState(dp, percent, true);
}
// === Manuelle Sperre prüfen
function isLockedManually(name) {
const lastManual = manualLock[name] || 0;
return Date.now() - lastManual < MANUAL_LOCK_DURATION;
}
// === Hauptlogik zur Steuerung der Jalousien ===
function checkJalousien(avgPVLong, avgPVShort) {
if (getState(CENTRAL_OVERRIDE).val === false) {
log('Zentral-Override aktiv – Automatische Steuerung deaktiviert.');
return;
}
const currentTemp = getState(TEMP_SENSOR_DP).val;
const now = new Date();
const currentHour = now.getHours();
if (currentTemp < TEMP_LIMIT) {
jalousien.forEach(j => {
if ((j.name === 'KA' || j.name === 'KI') && currentHour < SLEEPROOM_ACTIVE_HOUR) return;
if (jalousieStatus[j.name] === 'geschlossen' && !isLockedManually(j.name)) {
updateRolloStatus(j.name, 0);
setTimeout(() => {
setState(j.dpUp, 1);
setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
}, 0);
jalousieStatus[j.name] = 'offen';
openLock[j.name] = Date.now();
log(`[${j.name}] Temperatur unter Grenze – Jalousie wird geöffnet.`);
}
});
return;
}
jalousien.forEach(j => {
if ((j.name === 'KA' || j.name === 'KI') && currentHour < SLEEPROOM_ACTIVE_HOUR) return;
if (isLockedManually(j.name)) {
log(`[${j.name}] Manuelle Sperre aktiv – automatische Steuerung übersprungen.`);
return;
}
const thermo = getState(j.thermo).val;
const nowTime = Date.now();
const locked = nowTime - (jalousieLock[j.name] || 0) < LOCK_TIME;
const isClosed = jalousieStatus[j.name] === 'geschlossen';
const isOpen = jalousieStatus[j.name] === 'offen';
const openLocked = nowTime - (openLock[j.name] || 0) < OPEN_LOCK_TIME;
const lastCloseTimeDiff = nowTime - (lastCloseTime[j.name] || 0);
if (lastAvgPV !== null && lastAvgPV > PV_HIGH_THRESHOLD && avgPVShort < PV_LOW_THRESHOLD && lastAvgPVTime && (nowTime - lastAvgPVTime <= 2 * 60 * 1000) && !isOpen) {
updateRolloStatus(j.name, 0);
setState(j.dpUp, 1);
setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
jalousieStatus[j.name] = 'offen';
openLock[j.name] = nowTime;
log(`[${j.name}] Mittelwert fiel schnell – Jalousie wird geöffnet.`);
}
if (avgPVLong >= PV_HIGH_THRESHOLD && thermo === 0 && !isClosed && !locked && !openLocked) {
updateRolloStatus(j.name, 100);
setState(j.dpDown, 1);
setTimeout(() => setState(j.dpDown, 0), JALOUSIE_DURATION);
jalousieStatus[j.name] = 'geschlossen';
lastCloseTime[j.name] = nowTime;
jalousieLock[j.name] = nowTime;
log(`[${j.name}] Jalousie wird geschlossen.`);
}
if (avgPVLong < PV_OPEN_THRESHOLD && lastCloseTimeDiff >= LOCK_TIME && isClosed && !openLocked) {
updateRolloStatus(j.name, 0);
setState(j.dpUp, 1);
setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
jalousieStatus[j.name] = 'offen';
openLock[j.name] = nowTime;
log(`[${j.name}] Jalousie wird geöffnet (nach Sperre, unter 90% Schwellwert).`);
}
if (thermo === 1 && !isOpen && avgPVLong > PV_HIGH_THRESHOLD && !openLocked) {
updateRolloStatus(j.name, 0);
setState(j.dpUp, 1);
setTimeout(() => setState(j.dpUp, 0), JALOUSIE_OPEN_DURATION);
jalousieStatus[j.name] = 'offen';
openLock[j.name] = nowTime;
log(`[${j.name}] Thermostat aktiv – Jalousie wird geöffnet.`);
}
});
lastAvgPV = avgPVShort;
lastAvgPVTime = Date.now();
}
// === Reaktion auf neue PV-Daten (Trigger) ===
on({ id: PV_INPUT_DP, change: 'ne' }, function () {
const pvValue = getState(PV_INPUT_DP).val;
if (pvValue !== null && !isNaN(pvValue)) {
pvValues.push(pvValue);
pvLongTermValues.push(pvValue);
}
const avgPVShort = calculateMovingAverage(pvValues, 7);
const avgPVLong = calculateMovingAverage(pvLongTermValues, PV_LONG_TERM_VALUES);
setState(PV_AVG_DP, avgPVShort);
setState(PV_AVG_LONG_DP, avgPVLong);
lastChangeTime = Date.now();
checkJalousien(avgPVLong, avgPVShort);
});
// === Reaktion auf Änderung des Zentral-Override-Datenpunkts ===
on({ id: CENTRAL_OVERRIDE, change: 'ne' }, function (obj) {
const value = obj.state.val;
log(`[Zentral-Override] Automatik wurde ${value ? 'aktiviert' : 'deaktiviert'}.`);
});
// === Manuelle 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;
let movementTimer = null;
function stopMovement() {
clearTimeout(movementTimer);
setState(j.dpUp, 0);
setState(j.dpDown, 0);
movementTimer = null;
}
on({ id: dpUpGet, change: 'ne' }, function (obj) {
if (obj.state.val === 1) {
manualLock[j.name] = Date.now();
upStart = Date.now();
// nach 3 Sekunden: automatische Vollfahrt nach oben
setTimeout(() => {
if (getState(dpUpGet).val === 1 && movementTimer === null) {
const currentStatus = getState(j.statusDp).val || 100;
const duration = (currentStatus / 100) * JALOUSIE_OPEN_DURATION;
setState(j.dpUp, 1);
movementTimer = setTimeout(() => {
setState(j.dpUp, 0);
movementTimer = null;
jalousieStatus[j.name] = 'offen';
updateRolloStatus(j.name, 0);
}, duration);
}
}, 3000);
} else if (obj.state.val === 0 && upStart !== null) {
const duration = Date.now() - upStart;
const percent = Math.max(0, Math.min(100, 100 - Math.round((duration / JALOUSIE_OPEN_DURATION) * 100)));
updateRolloStatus(j.name, Math.round(percent / 10) * 10);
jalousieStatus[j.name] = percent === 0 ? 'offen' : 'teil_offen';
upStart = null;
}
});
on({ id: dpDownGet, change: 'ne' }, function (obj) {
if (obj.state.val === 1) {
manualLock[j.name] = Date.now();
downStart = Date.now();
// nach 3 Sekunden: automatische Vollfahrt nach unten
setTimeout(() => {
if (getState(dpDownGet).val === 1 && movementTimer === null) {
const currentStatus = getState(j.statusDp).val || 0;
const duration = ((100 - currentStatus) / 100) * JALOUSIE_DURATION;
setState(j.dpDown, 1);
movementTimer = setTimeout(() => {
setState(j.dpDown, 0);
movementTimer = null;
jalousieStatus[j.name] = 'geschlossen';
updateRolloStatus(j.name, 100);
}, duration);
}
}, 3000);
} else if (obj.state.val === 0 && downStart !== null) {
const duration = Date.now() - downStart;
const percent = Math.max(0, Math.min(100, Math.round((duration / JALOUSIE_DURATION) * 100)));
updateRolloStatus(j.name, Math.round(percent / 10) * 10);
jalousieStatus[j.name] = percent === 100 ? 'geschlossen' : 'teil_geschlossen';
downStart = null;
}
});
// Stoppt Fahrt bei erneutem Tastendruck
on({ id: [dpUpGet, dpDownGet], change: 'ne' }, function (obj) {
if (obj.state.val === 1 && movementTimer !== null) {
log(`[${j.name}] Fahrt gestoppt durch erneuten Tastendruck.`);
stopMovement();
}
});
});
----
Skasowano z tematu: Script to Control Electric Blinds Using PV Solar Output as Sunlight Sensor Input
przez io2345 dnia 23 Mai 2025 10:26