Hallo
da mir eine Systemneutralität am Herzen liegt und es hier um die optimale Regelung der Daikin per Faikout geht, mag ich sehr ähnliches für den IOBroker nicht vorenthalten. Blueprints stehen leider so nicht zur Verfügung und es ist letztlich effektiver direkt im sortierten und gut kommentierten JS-Code anzupassen.
/***************************************************
* =================================================
* DAIKIN KLIMA – EXTERNE REGELUNG
* =================================================
*
* FUNKTION:
* Steuert eine Daikin-Klimaanlage über vordefinierte Aliase.
* Enthält:
*
* 1️⃣ Zeitplanbetrieb:
* - Standard- und Luxusbetrieb nach Wochenplan
* - Aktiviert/Deaktiviert das Gerät je nach Uhrzeit
* - Setzt Solltemperatur beim Start des Zeitplans
* - Setzt Totband beim Start des Zeitplans
*
* 2️⃣ Swing + ECO Komfortfunktionen:
* - Lüfter wird automatisch auf AUTO gesetzt
* - Vertikaler Swing optional aktivieren
* - Swing wird nach Maximaldauer automatisch deaktiviert
* - Swing wird auch deaktiviert wenn Sollwert erreicht wird
* - ECO-Modus optional beim Betrieb aktivieren
*
* 3️⃣ Heizungsregelung (Demand-Regelung):
* - Aktiv nur im Heizmodus ("H")
* - Demand = Außentemperaturkennlinie + Soll/Ist-Korrektur
* - Demand wird auf Minimal/Maximalwert begrenzt
* - Rundung auf gültige 5%-Schritte
* - Überhitzschutz: Wenn Sollwert + Limit erreicht → Demand = Minimum
*
* 4️⃣ Manuelle Übersteuerung:
* - Setpoint-Änderungen an Gerät oder UI werden erkannt
* - Manueller Sollwert bleibt aktiv bis ein neuer Zeitplan startet
*
* 5️⃣ Zentrale Steuerung:
* - Nur ein Timer (mainLoop) steuert das komplette Script
* - Jede Minute werden Zeitplan und Regelung geprüft
*
***************************************************/
// =======================
// ALIASE – Steuerpunkte der Klimaanlage
// =======================
const ALIAS = {
power: "alias.0.WohnzimmerDaikin.power", // Anlage EIN/AUS
mode: "alias.0.WohnzimmerDaikin.modus", // Betriebsmodus (H/C/F)
fan: "alias.0.WohnzimmerDaikin.fanmodus", // Lüftermodus
swingv: "alias.0.WohnzimmerDaikin.swingv", // Vertikaler Swing
econo: "alias.0.WohnzimmerDaikin.econo", // ECO Modus
setTemp: "alias.0.WohnzimmerDaikin.autot", // Solltemperatur
deadband: "alias.0.WohnzimmerDaikin.autor", // Totband
demand: "alias.0.WohnzimmerDaikin.demand" // Leistungsanforderung %
};
// =======================
// SENSORPUNKTE – Messwerte
// =======================
const SENSOR = {
actualTemp: "alias.0.WohnzimmerEnv.ACTUAL", // aktuelle Raumtemperatur
outdoorTemp: "alias.0.WohnzimmerDaikin.Aussentemperatur" // Außentemperatur der Anlage
};
// =======================
// ZEITPLÄNE – Wochenbetrieb
// =======================
const ENABLE_STANDARD_SCHEDULE = true;
const ENABLE_LUXUS_SCHEDULE = true;
const SETPOINT_STANDARD = 21.2;
const SETPOINT_LUXUS = 21.5;
const DEADBAND = 0.2;
// Wochenplan (0 = Sonntag)
const scheduleConfig = {
startStandard: ["10:00","11:00","10:00","11:00","10:00","11:00","10:00"],
stopStandard: ["13:00","15:00","15:00","15:00","15:00","15:00","15:00"],
startLuxus: ["13:00","15:00","15:00","15:00","15:00","15:00","15:00"],
stopLuxus: ["21:45","21:45","21:45","21:45","21:45","22:00","22:15"]
};
// =======================
// SCHWINGAUTOMATIK + ECO
// =======================
const swingAuto = true; // Swing automatisch aktivieren
const swingMaxMinutes = 90; // maximale Swing-Dauer
const econoAuto = true; // ECO automatisch aktivieren
// =======================
// DEMAND – Leistungsregelung
// =======================
const DEMAND_MIN = 30;
const DEMAND_MAX = 100;
const DEMAND_STEP = 5;
const overheatLimit = 0.4; // Überhitzschutz
// Soll/Ist Korrektur
const correctionCurve = [
{ diff: -0.5, adjust: -10 },
{ diff: -0.2, adjust: -5 },
{ diff: 0.0, adjust: 0 },
{ diff: 0.35, adjust: 5 },
{ diff: 1.0, adjust: 10 }
];
// Basiskennlinie Außentemperatur
const demandCurve = [
{ temp: -10, demand: 70 },
{ temp: -5, demand: 60 },
{ temp: 0, demand: 55 },
{ temp: 5, demand: 50 },
{ temp: 10, demand: 45 },
{ temp: 15, demand: DEMAND_MIN }
];
// =======================
// INTERNER STATUS
// =======================
let lastScheduledSetpoint = null;
let manualOverrideActive = false;
let manualSetpoint = null;
let swingTimer = null;
let lastStdOn="", lastStdOff="", lastLuxOn="", lastLuxOff="";
// =======================
// HILFSFUNKTIONEN
// =======================
// Heizmodus prüfen
function isHeatingMode(){
return getState(ALIAS.mode)?.val === "H";
}
// ECO aktivieren
function ensureEcono(){
if(econoAuto && getState(ALIAS.econo)?.val !== true)
setState(ALIAS.econo,true,false);
}
// Komfortfunktionen aktivieren
function activateComfortFeatures(actualTemp,setpoint){
if(getState(ALIAS.fan)?.val !== "A")
setState(ALIAS.fan,"A",false);
if(swingAuto && getState(ALIAS.swingv)?.val!==true){
setState(ALIAS.swingv,true,false);
if(swingTimer) clearTimeout(swingTimer);
swingTimer=setTimeout(()=>{
setState(ALIAS.swingv,false,false);
},swingMaxMinutes*60*1000);
}
if(getState(ALIAS.swingv)?.val===true && actualTemp>=setpoint)
setState(ALIAS.swingv,false,false);
ensureEcono();
}
// =======================
// DEMAND BERECHNUNG
// =======================
// Basisleistung aus Außentemperatur
function calcBaseDemand(outTemp){
if(outTemp<=demandCurve[0].temp)
return demandCurve[0].demand;
if(outTemp>=demandCurve.at(-1).temp)
return demandCurve.at(-1).demand;
for(let i=0;i<demandCurve.length-1;i++){
const a=demandCurve[i];
const b=demandCurve[i+1];
if(outTemp>=a.temp && outTemp<=b.temp){
const ratio=(outTemp-a.temp)/(b.temp-a.temp);
return Math.round(a.demand + ratio*(b.demand-a.demand));
}
}
return DEMAND_MIN;
}
// Anpassung nach Soll/Ist
function adjustDemand(base,actual,target){
const diff=target-actual;
if(actual>=target+overheatLimit)
return DEMAND_MIN;
for(let i=0;i<correctionCurve.length-1;i++){
const a=correctionCurve[i];
const b=correctionCurve[i+1];
if(diff>=a.diff && diff<=b.diff){
const ratio=(diff-a.diff)/(b.diff-a.diff);
return Math.round(base+a.adjust + ratio*(b.adjust-a.adjust));
}
}
return base;
}
// =======================
// MANUELLE ÜBERSTEUERUNG
// =======================
function checkManualOverride(){
const currentSetpoint=parseFloat(getState(ALIAS.setTemp)?.val);
if(lastScheduledSetpoint!==null &&
currentSetpoint!==lastScheduledSetpoint &&
!manualOverrideActive){
manualOverrideActive=true;
manualSetpoint=currentSetpoint;
}
}
// =======================
// REGELUNG AUSFÜHREN
// =======================
function applyControl(setpoint){
if(!isHeatingMode()) return;
const actual=parseFloat(getState(SENSOR.actualTemp)?.val)||setpoint;
const outdoor=parseFloat(getState(SENSOR.outdoorTemp)?.val)||10;
if(manualOverrideActive)
setpoint=manualSetpoint;
else
lastScheduledSetpoint=setpoint;
setState(ALIAS.setTemp,setpoint,false);
let baseDemand=calcBaseDemand(outdoor);
let demand=adjustDemand(baseDemand,actual,setpoint);
demand=Math.round(demand/DEMAND_STEP)*DEMAND_STEP;
demand=Math.min(DEMAND_MAX,Math.max(DEMAND_MIN,demand));
setState(ALIAS.demand,demand,false);
activateComfortFeatures(actual,setpoint);
}
// =======================
// DEMAND NACHREGELN
// =======================
function updateDemand(){
if(!getState(ALIAS.power)?.val) return;
if(!isHeatingMode()) return;
const setpoint=parseFloat(getState(ALIAS.setTemp)?.val);
const actual=parseFloat(getState(SENSOR.actualTemp)?.val);
const outdoor=parseFloat(getState(SENSOR.outdoorTemp)?.val);
if(isNaN(setpoint)||isNaN(actual)) return;
let baseDemand=calcBaseDemand(outdoor);
let demand=adjustDemand(baseDemand,actual,setpoint);
demand=Math.round(demand/DEMAND_STEP)*DEMAND_STEP;
demand=Math.min(DEMAND_MAX,Math.max(DEMAND_MIN,demand));
setState(ALIAS.demand,demand,false);
activateComfortFeatures(actual,setpoint);
}
// =======================
// ZEITPLAN
// =======================
function checkSchedule(){
const now=new Date();
const wd=now.getDay();
const hm=now.toTimeString().slice(0,5);
const day=now.toDateString();
checkManualOverride();
if(ENABLE_STANDARD_SCHEDULE &&
hm>=scheduleConfig.startStandard[wd] &&
lastStdOn!==day){
manualOverrideActive=false;
setState(ALIAS.power,true,false);
setState(ALIAS.deadband,DEADBAND,false);
applyControl(SETPOINT_STANDARD);
lastStdOn=day;
}
if(ENABLE_LUXUS_SCHEDULE &&
hm>=scheduleConfig.startLuxus[wd] &&
lastLuxOn!==day){
manualOverrideActive=false;
setState(ALIAS.power,true,false);
setState(ALIAS.deadband,DEADBAND,false);
applyControl(SETPOINT_LUXUS);
lastLuxOn=day;
}
if(ENABLE_LUXUS_SCHEDULE &&
hm>=scheduleConfig.stopLuxus[wd] &&
lastLuxOff!==day){
setState(ALIAS.power,false,false);
lastLuxOff=day;
}
}
// =======================
// HAUPTSCHLEIFE
// =======================
function mainLoop(){
checkSchedule();
if(getState(ALIAS.power)?.val)
updateDemand();
}
// =======================
// TIMER – zentrale Steuerung
// =======================
schedule("* * * * *", mainLoop);