UNPKG

iobroker.heatingcontrol

Version:
1,227 lines (981 loc) 163 kB
/* eslint-disable prefer-template */ "use strict"; const utils = require("@iobroker/adapter-core"); const findObjectByKey = require("./support_tools.js").findObjectByKey; const findObjectsByKey = require("./support_tools.js").findObjectsByKey; const Check4ValidTemperature = require("./support_tools.js").Check4ValidTemperature; const CheckValidTime = require("./support_tools.js").CheckValidTime; const timeConverter = require("./support_tools.js").timeConverter; const GetTranslation = require("./support_tools.js").GetTranslation; const regulator_start = require("./regulators.js").regulator_start; const regulator_GetStatus = require("./regulators.js").regulator_GetStatus; const Vis_SetCurrentProfile = require("./vis.js").SetCurrentProfile; const Vis_SetCurrentTimePeriod = require("./vis.js").SetCurrentTimePeriod; const Vis_SetStatuslog = require("./vis.js").SetStatuslog; const Vis_SetTemperaturOverrideRemainingTime = require("./vis.js").SetTemperaturOverrideRemainingTime; const NumberOfPeriodsinVis = require("./cronjobs").NumberOfPeriodsinVis; const CreateCronJobs = require("./cronjobs").CreateCronJobs; const StartTimer2ResetFireplaceMode = require("./cronjobs").StartTimer2ResetFireplaceMode; const ErrorLog = require("./logging.js").ErrorLog; const ActorLog = require("./logging.js").ActorLog; const ThermostatLog = require("./logging.js").ThermostatLog; const WindowLog = require("./logging.js").WindowLog; const StartExtendedLog = require("./logging.js").StartExtendedLog; const SetSystemLanguage = require("./logging.js").SetSystemLanguage; const StartActors = require("./actor_ext.js").StartActors; const setActorState = require("./actor_ext.js").setActorState; const Path = require("node:path"); const csvLogger = require("./csvLogger.js").csvLogger; let parentAdapter; const ActorsWithoutThermostat = []; const Actors = []; const Sensors = []; const AddTempSensors = []; const Thermostats = []; const Rooms = []; let csvlogger = null; let SystemLanguage = "de"; //******************************************************************* // async function CreateDatabase(adapter, language) { parentAdapter = adapter; SystemLanguage = language; SetSystemLanguage(language); parentAdapter.log.info("start CreateDatabase with " + parentAdapter.config.rooms.length + " rooms"); StartExtendedLog(adapter); try { //create rooms for (let room = 0; room < parentAdapter.config.rooms.length; room++) { const roomName = parentAdapter.config.rooms[room].name || parentAdapter.config.rooms[room].Name; parentAdapter.log.info("room " + JSON.stringify(parentAdapter.config.rooms[room])); if (parentAdapter.config.rooms[room].isActive) { parentAdapter.log.info("active room found: " + roomName); let sensors = []; if (parentAdapter.config.UseSensors) { if (parentAdapter.config.rooms[room].WindowSensors !== undefined) { sensors = parentAdapter.config.rooms[room].WindowSensors; } } sensors = await GetSensorsId4Room(sensors, roomName); let addTempSensors = []; if (parentAdapter.config.UseAddTempSensors) { if (parentAdapter.config.rooms[room].AdditionalTemperatureSensors !== undefined) { addTempSensors = parentAdapter.config.rooms[room].AdditionalTemperatureSensors; } } addTempSensors = await GetAddTempSensorsId4Room(addTempSensors, roomName); let actors = []; if (parentAdapter.config.UseActors) { if (parentAdapter.config.rooms[room].Actors !== undefined) { actors = parentAdapter.config.rooms[room].Actors; } } actors = await GetActorsId4Room(actors, roomName); let thermostats = []; if (parentAdapter.config.rooms[room].Thermostats !== undefined) { thermostats = parentAdapter.config.rooms[room].Thermostats; } thermostats = await GetThermostatId4Room(thermostats, roomName); let HasActorsWithoutThermostat = false; if (actors.length > 0 && thermostats.length == 0) { HasActorsWithoutThermostat = true; for (let d = 0; d < actors.length; d++) { ActorsWithoutThermostat.push({ name: actors[d].name, id: actors[d].id, room: parentAdapter.config.rooms[room].name }); } } Rooms.push({ ID: room, Name: roomName , isActive: true, // standard: always active, possible to disable with DP WindowIsOpen: false, //heatingcontrol.0.Rooms.Büro.WindowIsOpen WindowIsOpenChanged: "never", TemperaturOverrideTime: "00:00", //heatingcontrol.0.Rooms.Büro.TemperaturOverrideTime TemperaturOverride: 0, //heatingcontrol.0.Rooms.Büro.TemperaturOverride State: "starting", //heatingcontrol.0.Rooms.Büro.State PreviousState: "none", CurrentTimePeriodTime: "00:00", //heatingcontrol.0.Rooms.Büro.CurrentTimePeriodTime CurrentTimePeriodFull: "", //heatingcontrol.0.Rooms.Büro.CurrentTimePeriodFull CurrentTimePeriod: -1, //heatingcontrol.0.Rooms.Büro.CurrentTimePeriod CurrentTarget: -99, //heatingcontrol.0.Rooms.Büro.CurrentTarget CurrentReduced: 0, ActiveTimeSlot: -1, //heatingcontrol.0.Rooms.Büro.ActiveTimeSlot MinimumTemperature: -99, //heatingcontrol.0.vis.RoomValues.MinimumTemperature CurrentProfileTarget: -99, // target according profile, used to disable ManuMode LastAutoTarget: -99, IsInOverride: false, // override active IsInReduced: false, // somehow reduced or increased CurrentReducedMode: "none", IsInManual: false, // target used from thermostat TemperatureManualMode: -99, CurrentTemperature: -99, CurrentTemperatureAddSensor: -99, ReducedState: "", HasActorsWithoutThermostat: HasActorsWithoutThermostat, //see issue #265 and #305 //WindowCloseTimerId: null, //WindowOpenTimerId: null, Present: true, VacationAbsent: false, MaintenanceActive: false, PowerInterruptionPeriodActive: false, FireplaceModeActive: false, HolidayPresent: false, GuestsPresent: false, PartyNow: false, PublicHolidyToday: false, OverrideTimerId: null, HeatingPeriod: true, StatusLog: [], hasWindowSensors: (sensors.length > 0 ? true : false), ActorState: false, NextTemperaturFromThermostatIsWindowOpen: false, //used to identify Temperatur from thermostat as a WindowOpenTemperature WindowOpenUntilTemperaturTimerId: null, sensors: sensors, TempOffset: 0, // to correct current temperatur from thermostat with a additional sensor hasAddTempSensors: (addTempSensors.length > 0 ? true : false), TempOffsetData: [], RemainingOverrideTime: 0, RemainingOverrideTimerId: null, WaitForTempIfWindowOpen: parentAdapter.config.rooms[room].WaitForTempIfWindowOpen, //time to wait for temperature update from thermostat when thermostat handles WindowIsOpen TemperatureIfNoHeatingPeriod: -99, csvLoggerIdx: -1, csvLoggerFile:"" }); //test only //parentAdapter.log.warn("WaitForTempIfWindowOpen: " + parentAdapter.config.rooms[room].WaitForTempIfWindowOpen); } } if (Rooms.length > 0) { parentAdapter.log.debug("CreateDatabase: " + Rooms.length + " active rooms found " + JSON.stringify(Rooms)); } else { parentAdapter.log.warn("CreateDatabase: none active rooms found! " + JSON.stringify(Rooms)); } parentAdapter.log.debug("CreateDatabase: " + Sensors.length + " active sensors found " + JSON.stringify(Sensors)); parentAdapter.log.debug("CreateDatabase: " + Actors.length + " active actors found " + JSON.stringify(Actors)); if (Thermostats.length > 0) { parentAdapter.log.debug("CreateDatabase: " + Thermostats.length + " active thermostats found " + JSON.stringify(Thermostats)); } else { parentAdapter.log.warn("CreateDatabase: none active thermostats found! " + JSON.stringify(Thermostats)); } parentAdapter.log.debug("CreateDatabase: " + AddTempSensors.length + " active additional Temp sensors found " + JSON.stringify(AddTempSensors)); if (parentAdapter.config.UseActors) { parentAdapter.log.debug("starting regulator"); regulator_start(parentAdapter); } StartActors(parentAdapter, ErrorLog); //csv logging starten if (parentAdapter.config.enableCSVLogging) { parentAdapter.log.debug("starting CSV logging"); csvlogger = new csvLogger(parentAdapter); const header = [ { id: "Time", title: "Time" }, { id: "currentTemp", title: "currentTemp" }, { id: "targetTemp", title: "targetTemp" } ]; for (let room = 0; room < Rooms.length; room++) { //pro raum ein log file // \iobroker\iobroker - data\files\ai - heatingcontrol\data\TempPrediction_Wohnzimmer.csv //const CSVPath = Path.join(parentAdapter.config.CustomCsvPath, "currentProfile.csv"); const filename = "TempLog_" + Rooms[room].Name + ".csv"; const CSVPath = Path.normalize(Path.join(utils.getAbsoluteDefaultDataDir(), 'files', 'heatingcontrol', 'data', filename)); Rooms[room].csvLoggerFile = CSVPath; Rooms[room].csvLoggerIdx=csvlogger.StartLog(CSVPath, header); } } parentAdapter.log.debug(JSON.stringify(ActorsWithoutThermostat)); parentAdapter.log.debug(JSON.stringify(Actors)); parentAdapter.log.debug(JSON.stringify(Sensors)); parentAdapter.log.debug(JSON.stringify(AddTempSensors)); parentAdapter.log.debug(JSON.stringify(Thermostats)); parentAdapter.log.debug(JSON.stringify(Rooms)); } catch (e) { parentAdapter.log.error("exception in CreateDatabase [" + e + "]"); } parentAdapter.log.info("CreateDatabase done with " + Rooms.length + " rooms"); } function WriteCsvLog(roomCsvLogId, currentTemp, targetTemp) { if (parentAdapter.config.enableCSVLogging) { const now = new Date().toISOString(); const records = [ { Time: now, currentTemp: currentTemp, targetTemp: targetTemp } ]; //idx pro raum if (roomCsvLogId >= 0) { csvlogger.WriteCSVLog(roomCsvLogId, records); } else { parentAdapter.log.error( " unknown csv logger idx"); } } } async function GetSensorsId4Room(sensors, room) { if (parentAdapter.config.UseSensors) { for (let d = 0; d < sensors.length; d++) { if (sensors[d].isActive) { let DataType = "boolean"; let valueOpen = true; let valueClosed = false; if (sensors[d].DataType !== undefined && sensors[d].DataType != null) { DataType = sensors[d].DataType; valueOpen = sensors[d].valueOpen; valueClosed = sensors[d].valueClosed; } else { parentAdapter.log.warn("window sensor has no datatyp in (" + sensors[d].name + "), boolean used. Please check and save configuration"); } Sensors.push({ name: sensors[d].name, room: room, OID: sensors[d].OID_Current, lastState: false, lastChange: "", DataType: DataType, valueOpen: valueOpen, valueClosed: valueClosed, //see issue #265 and #305 WindowCloseTimerId: null, WindowOpenTimerId: null, }); sensors[d].state = -1; sensors[d].id = Sensors.length - 1; sensors[d].OID = sensors[d].OID_Current; } } } parentAdapter.log.debug("got sensors for " + room + " " + JSON.stringify(sensors) + " " + JSON.stringify(Sensors)); return sensors; } /* async function GetSensorsId4Room_old( room) { const sensors = []; if (parentAdapter.config.UseSensors) { const devices = findObjectsByKey(parentAdapter.config.devices, "room", room); if (devices !== null) { for (let d = 0; d < devices.length; d++) { if (devices[d].type == 3 && devices[d].isActive) { let DataType = "boolean"; let valueOpen = true; let valueClosed = false; if (devices[d].DataType !== undefined && devices[d].DataType != null) { DataType = devices[d].DataType; valueOpen = devices[d].valueOpen; valueClosed = devices[d].valueClosed; } else { parentAdapter.log.warn("window sensor has no datatyp in " + room + " (" + devices[d].name + "), boolean used. Please check and save configuration"); } Sensors.push({ name: devices[d].name, room: room, OID: devices[d].OID_Current, lastState: false, lastChange: "", DataType: DataType, valueOpen: valueOpen, valueClosed: valueClosed, //see issue #265 and #305 WindowCloseTimerId: null, WindowOpenTimerId: null, }); sensors.push({ name: devices[d].name, id: Sensors.length - 1, OID: devices[d].OID_Current, DataType: DataType, valueOpen: valueOpen, valueClosed: valueClosed, state: -1 }); } } } } //adapter.log.debug("got sensors for " + room + "" + JSON.stringify(sensors) + " " + JSON.stringify(Sensors)); return sensors; } */ async function GetAddTempSensorsId4Room(sensors, room) { if (parentAdapter.config.UseAddTempSensors) { for (let d = 0; d < sensors.length; d++) { if (sensors[d].isActive) { AddTempSensors.push({ name: sensors[d].name, room: room, OID: sensors[d].OID_Current, lastState: false, lastChange: "" }); sensors[d].id = Sensors.length - 1; sensors[d].OID = sensors[d].OID_Current; } } } parentAdapter.log.debug("got sensors for " + room + " " + JSON.stringify(sensors) + " " + JSON.stringify(Sensors)); return sensors; } /* async function GetAddTempSensorsId4Room_old(room) { const sensors = []; if (parentAdapter.config.UseAddTempSensors) { const devices = findObjectsByKey(parentAdapter.config.devices, "room", room); if (devices !== null) { for (let d = 0; d < devices.length; d++) { if (devices[d].type == 4 && devices[d].isActive) { AddTempSensors.push({ name: devices[d].name, room: room, OID: devices[d].OID_Current, lastState: false, lastChange: "" }); sensors.push({ name: devices[d].name, id: Sensors.length - 1, OID: devices[d].OID_Current }); } } } } //parentAdapter.log.warn("got sensors for " + room + " " + JSON.stringify(sensors) + " " + JSON.stringify(Sensors)); return sensors; } */ async function GetActorsId4Room(actors, room) { if (parentAdapter.config.UseActors) { for (let d = 0; d < actors.length; d++) { if (actors[d].isActive) { Actors.push({ name: actors[d].name, room: room, OID: actors[d].OID_Target, lastState: false, lastChange: "", ActorOnTimerId: null, ActorOffTimerId: null, //for extended actor handling useExtHandling: actors[d].useExtHandling, reptime: parentAdapter.config.ExtHandlingActorRepTime, ackwait: parentAdapter.config.ExtHandlingActorAckWaitTime, ExtSetTimerId: null, SetCnt: 0 }); actors[d].id = Actors.length-1; } } } parentAdapter.log.debug("got actors for " + room + " " + JSON.stringify(actors) + " " + JSON.stringify(Actors)); return actors; } /* async function GetActorsId4Room_old(room) { const actors = []; if (parentAdapter.config.UseActors) { const devices = findObjectsByKey(parentAdapter.config.devices, "room", room); if (devices !== null) { for (let d = 0; d < devices.length; d++) { if (devices[d].type == 2 && devices[d].isActive) { Actors.push({ name: devices[d].name, room: room, OID: devices[d].OID_Target, lastState: false, lastChange: "", ActorOnTimerId: null, ActorOffTimerId: null, //for extended actor handling useExtHandling: devices[d].useExtHandling, reptime: parentAdapter.config.ExtHandlingActorRepTime, ackwait: parentAdapter.config.ExtHandlingActorAckWaitTime, ExtSetTimerId: null, SetCnt: 0 }); actors.push({ name: devices[d].name, id: Actors.length - 1 }); } } } } //adapter.log.debug("got actors for " + room + "" + JSON.stringify(actors) + " " + JSON.stringify(Actors)); return actors; } */ async function GetThermostatId4Room(thermostats, room) { for (let d = 0; d < thermostats.length; d++) { if (thermostats[d].isActive) { Thermostats.push({ name: thermostats[d].name, room: room, OID_Target: thermostats[d].OID_Target, OID_Current: thermostats[d].OID_Current, lastTarget: -99, currentTarget: -99, lastTargetFromDevice: -99, lastChange: "", SetTimerId: null, //for extended actor handling useExtHandling: thermostats[d].useExtHandling, reptime: parentAdapter.config.ExtHandlingThermostatRepTime, ackwait: parentAdapter.config.ExtHandlingThermostatAckWaitTime, ExtSetTimerId: null, SetCnt: 0 }); thermostats[d].id = Thermostats.length - 1; } } parentAdapter.log.debug("got thermostats for " + room + " " + JSON.stringify(thermostats) + " " + JSON.stringify(Thermostats)); return thermostats; } /* async function GetThermostatId4Room_old(room) { const thermostats = []; const devices = findObjectsByKey(parentAdapter.config.devices, "room", room); if (devices !== null) { for (let d = 0; d < devices.length; d++) { if (devices[d].type == 1 && devices[d].isActive) { Thermostats.push({ name: devices[d].name, room: room, OID_Target: devices[d].OID_Target, OID_Current: devices[d].OID_Current, lastTarget: -99, currentTarget: -99, lastTargetFromDevice: -99, lastChange: "", SetTimerId: null, //for extended actor handling useExtHandling: devices[d].useExtHandling, reptime: parentAdapter.config.ExtHandlingThermostatRepTime, ackwait: parentAdapter.config.ExtHandlingThermostatAckWaitTime, ExtSetTimerId: null, SetCnt: 0 }); thermostats.push({ name: devices[d].name, id: Thermostats.length - 1 }); } } } //parentAdapter.log.debug("got thermostats for " + room + "" + JSON.stringify(thermostats) + " " + JSON.stringify(Thermostats)); return thermostats; } */ //******************************************************************* // function GetUsedRooms() { let UsedRooms = ""; for (let room = 0; room < Rooms.length; room++) { UsedRooms += Rooms[room].Name; UsedRooms += ";"; } if (UsedRooms != null && UsedRooms.length > 0) { //remove last ; UsedRooms = UsedRooms.slice(0, -1); } return UsedRooms; } //******************************************************************* // let alreadyChanging = false; const ChangeStatusCmdList = []; const MaxCmdList = 100; async function ChangeStatus(state, room, value) { if (alreadyChanging) { if (ChangeStatusCmdList.length < MaxCmdList) { ChangeStatusCmdList.push( { state: state, room: room, value: value } ); parentAdapter.log.debug("ChangeStatus called, while already changing, push to list " + room + " " + state + " " + JSON.stringify(value) + " (" + ChangeStatusCmdList.length + ")"); } else { parentAdapter.log.error("ChangeStatusCmdList is longer then " + MaxCmdList + " entries, no new entry allowed -> skipped " + room + " " + state + " " + JSON.stringify(value)); } return; } //only one change call is allowed, put other in a list alreadyChanging = true; if (room == "all") { for (room = 0; room < Rooms.length; room++) { await ChangeStatus_Part2(state, Rooms[room].Name, value); } } else { await ChangeStatus_Part2(state, room, value); } alreadyChanging = false; if (ChangeStatusCmdList.length > 0) { const cmd = ChangeStatusCmdList.shift(); parentAdapter.log.debug("ChangeStatus recall cmd from list " + cmd.room + " " + cmd.state + " " + JSON.stringify(cmd.value) + " (" + ChangeStatusCmdList.length + ")"); await ChangeStatus(cmd.state, cmd.room, cmd.value); } } async function ChangeStatus_Part2(state, room, value) { try { const theRoom = findObjectByKey(Rooms, "Name", room); let val; if (value != null) { if (typeof value == "object") { val = value.val; } else { val = value; } } let changed = false; let handleActors = false; if (theRoom != null) { parentAdapter.log.debug(theRoom.Name + " ### ChangeStatus " + state + " to " + JSON.stringify(value) + " in " + theRoom.State); if (state == "isActive") { theRoom.isActive = value; if (!theRoom.isActive) { //see issue #332: deactivate override before deactivate heating if (theRoom.OverrideTimerId != null) { clearTimeout(theRoom.OverrideTimerId); theRoom.OverrideTimerId = null; } SetNewRoomState(theRoom, "no heating"); changed = true; } else if (theRoom.State == "no heating") { SetNewRoomState(theRoom, "auto"); changed = true; } } else if (state == "Sensor") { //erst in SensorListe des Raumes eintragen und dann neues WindowIsOpen prüfen const newVal = AddSensorState(theRoom, value); if (theRoom.WindowIsOpen != newVal) { WindowLog(theRoom.Name, newVal); theRoom.WindowIsOpen = newVal; theRoom.WindowIsOpenChanged = timeConverter(SystemLanguage, null); //Pittini vis if (parentAdapter.config.UseVisFromPittini) { await CreateWindowStatesTable(); } //====================================================== //special behavior for Thermostats which handles window open if (parentAdapter.config.ThermostatHandlesWindowOpen) { if (theRoom.WindowIsOpen) { parentAdapter.log.debug(theRoom.Name + " NextTemperaturFromThermostatIsWindowOpen start timer"); //if window is opened wait for reduced target temperature from thermostat (max. 10 seconds) theRoom.NextTemperaturFromThermostatIsWindowOpen = true; if (theRoom.WindowOpenUntilTemperaturTimerId != null) { //cancel and restart clearTimeout(theRoom.WindowOpenUntilTemperaturTimerId); theRoom.WindowOpenUntilTemperaturTimerId = null; } let WaitForTempIfWindowOpen = 10000; //default 10 sek if (theRoom.WaitForTempIfWindowOpen !== undefined && theRoom.WaitForTempIfWindowOpen > 0) { WaitForTempIfWindowOpen = theRoom.WaitForTempIfWindowOpen * 1000; } //braucht es das wirklich? ohne würden Änderungen vom Thermostat ignoriert werden, solange das Fenster auf ist theRoom.WindowOpenUntilTemperaturTimerId = setTimeout(WindowOpenUntilTemperaturTimeout, WaitForTempIfWindowOpen, theRoom.Name); } else { //if window is closed, set back to auto mode (but only when window is open, ignore it if other reduced states are active) if (theRoom.ReducedState == "WindowOpen") { parentAdapter.log.debug(theRoom.Name + " NextTemperaturFromThermostatIsWindowOpen stop timer"); //see issue #347: andernfalls kommen alle anderen Änderungen vom Thermostat als auto WindowOpen theRoom.NextTemperaturFromThermostatIsWindowOpen = false; theRoom.ReducedState = ""; theRoom.IsInReduced = false; theRoom.CurrentTarget = theRoom.CurrentProfileTarget; } } } //====================================================== changed = true; } } else if (state == "AddTempSensor") { await CalculateTempOffset(theRoom, value); if (parentAdapter.config.AddTempSensorsUseEveryOffsetChange) { parentAdapter.log.debug(theRoom.Name + " offset calc done, use new offset "); changed = true; handleActors = true; } } else if (state == "TemperatureOffset") { //ChangeStatus not implemented yet TemperatureOffset Wohnzimmer value 22 theRoom.TempOffset = value; } else if (state == "Thermostats_Current") { parentAdapter.log.debug("Change Status current temperature in " + theRoom.Name + " to " + val); theRoom.CurrentTemperature = val; handleActors = true; } else if (state == "Thermostats_Target") { parentAdapter.log.debug("Change Status target temperature in " + theRoom.Name + " to " + val + " (profile " + theRoom.CurrentProfileTarget + ") in " + theRoom.State); if (theRoom.State != "starting") { const result = await CheckManualMode(theRoom, val); changed = result.changed; handleActors = result.handleActors; } } else if (state == "ResetManual") { parentAdapter.log.debug("ChangeStatus ResetManual"); //see issue #355 manche verwenden den Button um override zu beenden //wird hier aber gar nicht behandelt??? if (theRoom.IsInOverride) { parentAdapter.log.debug("StopOverride in ResetManual"); theRoom.IsInOverride = false; theRoom.overrideTemp = 0; } theRoom.State = "auto"; changed = true; } else if (state == "Profiles" || state == "CurrentProfile") { parentAdapter.log.debug("ChangeStatus Profile or CurrentProfile"); if (theRoom.State != "starting") { const currentProfile = await GetCurrentProfile(); await CreateCronJobs(parentAdapter, currentProfile, ChangeStatus, Rooms); //aktuellen Profilpunkt setzen await GetCurrentProfilePoint(theRoom, currentProfile); } changed = true; } else if (state == "TemperaturOverride" || state == "TemperaturOverrideTime") { parentAdapter.log.debug("ChangeStatus Override "); if (theRoom.State != "starting") { await CheckOverride(theRoom); } changed = true; } else if (state == "StopOveride") { parentAdapter.log.debug("ChangeStatus StopOverride "); theRoom.IsInOverride = false; theRoom.overrideTemp = 0; theRoom.State = "auto"; changed = true; } else if (state == "ProfilPoint") { if (value.target !== undefined && !isNaN(value.target)) { //this comes from cron job if (parseInt(parentAdapter.config.OverrideMode) === 2) { if (theRoom.IsInOverride) { parentAdapter.log.debug(theRoom.Name + " new profile point, but still in override: need to stop override first"); theRoom.IsInOverride = false; theRoom.overrideTemp = 0; theRoom.State = "auto"; } } theRoom.CurrentProfileTarget = Check4ValidTemperature(parentAdapter, value.target); theRoom.CurrentTimePeriod = value.currentTimePeriod; theRoom.ActiveTimeSlot = value.ActiveTimeSlot; theRoom.CurrentTimePeriodTime = value.CurrentTimePeriodTime; theRoom.CurrentTimePeriodFull = value.CurrentTimePeriodFull; if (theRoom.State == "manual") { theRoom.State = "auto"; parentAdapter.log.debug("reset manual mode to auto"); } parentAdapter.log.debug("ChangeStatus by cron Profilepoint in " + theRoom.Name + " target " + theRoom.CurrentProfileTarget); changed = true; } else if (val == -99) { //=-99 just triggers recalculation parentAdapter.log.debug("ChangeStatus Profilepoint in " + theRoom.Name + " just recalc"); changed = true; } else { //initial when adapter starts parentAdapter.log.debug("ChangeStatus Profilepoint in " + theRoom.Name + " target " + val); theRoom.CurrentProfileTarget = Check4ValidTemperature(parentAdapter, val); changed = true; } } else if (state == "Present") { if (theRoom.Present != val) { parentAdapter.log.debug("Change Status Present in " + theRoom.Name + " to " + val); theRoom.Present = val; changed = true; } } else if (state == "VacationAbsent") { if (theRoom.VacationAbsent != val) { parentAdapter.log.debug("Change Status VacationAbsent in " + theRoom.Name + " to " + val); theRoom.VacationAbsent = val; changed = true; } } else if (state == "MaintenanceActive") { if (theRoom.MaintenanceActive != val) { parentAdapter.log.debug("Change Status MaintenanceActive in " + theRoom.Name + " to " + val); theRoom.MaintenanceActive = val; changed = true; } } else if (state == "PowerInterruptionPeriodActive") { if (theRoom.PowerInterruptionPeriodActive != val) { parentAdapter.log.debug("Change Status PowerInterruptionPeriodActive in " + theRoom.Name + " to " + val); theRoom.PowerInterruptionPeriodActive = val; changed = true; if (theRoom.PowerInterruptionPeriodActive) { SetNewRoomState(theRoom, "PowerInterruption"); } } } else if (state == "FireplaceModeActive") { if (theRoom.FireplaceModeActive != val) { parentAdapter.log.debug("Change Status FireplaceModeActive in " + theRoom.Name + " to " + val); theRoom.FireplaceModeActive = val; changed = true; } } else if (state == "HolidayPresent") { if (theRoom.HolidayPresent != val) { parentAdapter.log.debug("Change Status HolidayPresent in " + theRoom.Name + " to " + val); theRoom.HolidayPresent = val; //2024-02-25 warum beim starten nicht? wir brauchen die cron job Berechnung auch hier... //if (theRoom.State != "starting") { //Neuberechnung der Zeiten (cron) und damit Zieltemperaturen const currentProfile = await GetCurrentProfile(); await CreateCronJobs(parentAdapter, currentProfile, ChangeStatus, Rooms); //aktuellen Profilpunkt setzen await GetCurrentProfilePoint(theRoom, currentProfile); changed = true; //} } } else if (state == "GuestsPresent") { if (theRoom.GuestsPresent != val) { parentAdapter.log.debug("Change Status GuestsPresent in " + theRoom.Name + " to " + val); theRoom.GuestsPresent = val; changed = true; } } else if (state == "PartyNow") { if (theRoom.PartyNow != val) { parentAdapter.log.debug("Change Status PartyNow in " + theRoom.Name + " to " + val); theRoom.PartyNow = val; changed = true; } } else if (state == "PublicHolidyToday") { if (theRoom.PublicHolidyToday != val) { parentAdapter.log.info("Change Status PublicHolidyToday in " + theRoom.Name + " to " + val); theRoom.PublicHolidyToday = val; //2024-02-25 warum beim starten nicht? wir brauchen die cron job Berechnung auch hier... //if (theRoom.State != "starting") { //Neuberechnung der Zeiten (cron) und damit Zieltemperaturen const currentProfile = await GetCurrentProfile(); await CreateCronJobs(parentAdapter, currentProfile, ChangeStatus, Rooms); //aktuellen Profilpunkt setzen await GetCurrentProfilePoint(theRoom, currentProfile); changed = true; //} } } else if (state == "HeatingPeriodActive") { if (theRoom.HeatingPeriod != val) { parentAdapter.log.info("Change Status HeatingPeriodActive in " + theRoom.Name + " to " + val); theRoom.HeatingPeriod = val; changed = true; if (theRoom.State != "starting") { if (theRoom.HeatingPeriod) { SetNewRoomState(theRoom, "auto"); } else { SetNewRoomState(theRoom, "no heating"); } } } } else if (state == "MinimumTemperature") { theRoom.MinimumTemperature = val; } else if (state == "TemperatureIfNoHeatingPeriod") { theRoom.TemperatureIfNoHeatingPeriod = val; } else { parentAdapter.log.error("!!ChangeStatus not implemented yet " + state + " " + theRoom.Name + " value " + JSON.stringify(value)); } if (theRoom.State != "starting" && changed) { const temperature = await CalculateRoomTemperature(theRoom); await SetRoomTemperature(theRoom, temperature); handleActors = true; } if (theRoom.State != "starting" && handleActors) { await HandleActors(theRoom); await HandleActorsWithoutThermostat(); await CheckAllActors(); WriteCsvLog(theRoom.csvLoggerIdx, theRoom.CurrentTemperature, theRoom.CurrentTarget); } await UpdateDPs(theRoom); } else { parentAdapter.log.warn("ChangeStatus: room " + room + " not found"); } } catch (e) { parentAdapter.log.error("exception in ChangeStatus [" + e + "] " + state + " in " + room + " " + JSON.stringify(value)); } } async function GetTarget4NoHeatingPeriod(room) { let temp2Set = -99; if (parentAdapter.config.ThermostatModeIfNoHeatingperiod == 1) { const id = "Rooms." + room.Name + ".TemperatureIfNoHeatingPeriod"; const TargetTemp = await parentAdapter.getStateAsync(id); if (TargetTemp != null && typeof TargetTemp == "object") { parentAdapter.log.debug("set target (1) if no heating for room " + room.Name + " to " + TargetTemp.val); temp2Set = TargetTemp.val; } else { parentAdapter.log.error("target temperature for no heating period is not set for " + room.Name); } } else if (parentAdapter.config.ThermostatModeIfNoHeatingperiod == 2) { const TargetTemp = parseFloat(parentAdapter.config.FixTempIfNoHeatingPeriod); parentAdapter.log.debug("set target (2) if no heating for room " + room.Name + " to " + TargetTemp); temp2Set = TargetTemp; } else { parentAdapter.log.debug("do not set target if no heating for room " + room.Name); } return temp2Set; } async function CalculateRoomTemperature(room) { parentAdapter.log.debug("CalculateRoomTemperature for " + room.Name + " " + room.State + " " + room.WindowIsOpen); let Temp2Set = -99; room.IsInReduced = false; //rücksetzen ... if (room.State == "Maintenance" && !room.MaintenanceActive) { SetNewRoomState(room, room.PreviousState); } //else //kein else!! if (room.MaintenanceActive) { SetNewRoomState(room, "Maintenance"); Temp2Set = parentAdapter.config.MaintenanceModeTemperature; } else if (room.State == "no heating" || !room.HeatingPeriod) { if (room.State != "no heating") { SetNewRoomState(room, "no heating"); } Temp2Set = await GetTarget4NoHeatingPeriod(room); } else if (room.State == "auto" || room.State == "PowerInterruption") { Temp2Set = room.CurrentProfileTarget; parentAdapter.log.debug(room.Name + " auto mode: target " + Temp2Set); if (room.WindowIsOpen) { if (!parentAdapter.config.ThermostatHandlesWindowOpen) { const preTemp2Set = await GetReducedTemperature(room.Name, "WindowOpenDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "WindowOpen"; room.IsInReduced = true; } } else { parentAdapter.log.debug(room.Name + " we are in ThermostatHandlesWindowOpen"); room.ReducedState = "WindowOpen"; room.IsInReduced = true; Temp2Set = await GetCurrentTarget(room); } } //Reihenfolge nochmal prüfen? //see issue #285 absent and VacationAbsent exchanged if (!room.IsInReduced && room.VacationAbsent) { const preTemp2Set = await GetReducedTemperature(room.Name, "VacationAbsentDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "VacationAbsent"; room.IsInReduced = true; } } if (!room.IsInReduced && !room.Present) { const preTemp2Set = await GetReducedTemperature(room.Name, "AbsentDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "Absent"; room.IsInReduced = true; } } if (!room.IsInReduced && room.GuestsPresent) { const preTemp2Set = await GetReducedTemperature(room.Name, "GuestIncrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set += preTemp2Set.target; room.CurrentReduced = preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "Guests"; room.IsInReduced = true; } } if (!room.IsInReduced && room.PartyNow) { const preTemp2Set = await GetReducedTemperature(room.Name, "PartyDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "Party"; room.IsInReduced = true; } } if (!room.IsInReduced && room.FireplaceModeActive) { const preTemp2Set = await GetReducedTemperature(room.Name, "FireplaceModeDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "FireplaceMode"; room.IsInReduced = true; } } parentAdapter.log.debug(room.Name + " auto mode (incl. reduced): target " + Temp2Set); room.LastAutoTarget = Temp2Set; } else if (room.State == "override") { //Küche override mode(incl.reduced): target 11(window open false 11 Temp2Set = room.TemperaturOverride; //in override only window sensor is checked if (room.WindowIsOpen) { const preTemp2Set = await GetReducedTemperature(room.Name, "WindowOpenDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; } else { Temp2Set = preTemp2Set.target; } room.ReducedState = "WindowOpen"; room.IsInReduced = true; } } parentAdapter.log.debug(room.Name + " override mode (incl. reduced): target " + Temp2Set + " ( window open " + room.WindowIsOpen + " " + room.TemperaturOverride +")"); } else if (room.State == "manual") { Temp2Set = room.TemperatureManualMode; parentAdapter.log.debug(room.Name + " CalculateRoomTemperature for manual " + Temp2Set); //in manual mode check only window sensors if (room.WindowIsOpen) { const preTemp2Set = await GetReducedTemperature(room.Name, "WindowOpenDecrease"); if (preTemp2Set.target != 0 && preTemp2Set.decreaseMode != "none") { room.CurrentReducedMode = preTemp2Set.decreaseMode; if (preTemp2Set.decreaseMode == "rel") { Temp2Set -= preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; room.ReducedState = "WindowOpen"; room.IsInReduced = true; } else { //irgendetwas war da, warum ich das nicht so wollte??? Temp2Set = preTemp2Set.target; room.CurrentReduced = -1 * preTemp2Set.target; room.ReducedState = "WindowOpen"; room.IsInReduced = true; } } } } else if (room.State == "starting") { //do nothing } else { parentAdapter.log.warn(room.Name + " not supported room state " + room.State); } if (Temp2Set > -99) { Temp2Set = await CheckMinTemp(room.Name, Temp2Set); } return Temp2Set; } async function GetReducedTemperature(room, parameter) { let decreaseMode = "none"; let target = 0; try { const currentProfile = await GetCurrentProfile(); let id = "Profiles." + currentProfi