iobroker.heatingcontrol
Version:
HeatingControl Adapter
1,227 lines (981 loc) • 163 kB
JavaScript
/* 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