-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathheaterFunction.js
203 lines (170 loc) · 7.05 KB
/
heaterFunction.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**One thhing of note I use a traditional automation to update my
* highest soil sensor. I find this is easy just to do through the gui
*/
// Constants for Home Assistant Entity IDs
const ENTITY_IDS = {
heaterPumpSwitch: 'switch.heater_pump',
minLightsOnTemp: 'input_number.min_lights_on_temp',
minLightsOffTemp: 'input_number.min_lights_off_temp',
roomTempSensor: 'sensor.room_temp',
lightOnTime: 'input_datetime.light_on_time',
darkHours: 'input_number.side_1_dark_hours',
};
const delta= 2; //delta before turning on heater
const debug = true;
// For retrieving data:
let roomTempSensor = getHAState(ENTITY_IDS.roomTempSensor);
let minLightsOnTemp = getHAState(ENTITY_IDS.minLightsOnTemp);
let minLightsOffTemp = getHAState(ENTITY_IDS.minLightsOffTemp);
let heaterPumpSwitch = getHAState(ENTITY_IDS.heaterPumpSwitch);
let lightOnTime = getHAState(ENTITY_IDS.lightOnTime);
let darkHours = getHAState(ENTITY_IDS.darkHours);
/**
*
* Nothing needs to be changed under this section unless your modifing
* the basic functionality or how the script works.
* Modifiy at your own risk
*
*/
// Function to retrieve state from Home Assistant
function getHAState(state) {
// Check if the states object and the specific state exist
if (global.get('homeassistant') && global.get('homeassistant').homeAssistant
&& global.get('homeassistant').homeAssistant.states
&& global.get('homeassistant').homeAssistant.states[state]) {
return global.get('homeassistant').homeAssistant.states[state].state;
} else {
// Handle the case where the state or any parent object is undefined
node.warn("State not found or global object is undefined: " + state);
return null; // or you can throw an error or return a default value
}
}
//check for null states
function checkForNullStates() {
let nullStates = [];
if (roomTempSensor === null) nullStates.push("RoomTempSensor");
if (heaterPumpSwitch === null) nullStates.push("heaterPumpSwitch");
if (minLightsOnTemp === null) nullStates.push("minLightsOnTemp");
if (minLightsOffTemp === null) nullStates.push("minLightsOffTemp");
if (lightOnTime === null) nullStates.push("lightOnTime");
if (darkHours === null) nullStates.push("darkHours");
return nullStates; // Returns an array of null state names, empty if none are null
}
// Enhanced logging for debugging
function logDebugData() {
if (debug) {
node.warn("Room Temp Sensor: " + roomTempSensor); //room temp
node.warn("Heater Pump Switch: " + heaterPumpSwitch);
node.warn("Min Lights On Temp: " + minLightsOnTemp);
node.warn("Min Lights Off Temp: " + minLightsOffTemp);
node.warn("Light On Time: " + lightOnTime);
node.warn("Dark Hours: " + darkHours);
node.warn("Is Lights On Time: " + checkLightsOnTime());
}
}
//function to make logbook entries
function logbookMsg(message) {
// Create a message object with the payload for the api-call-service node
const logMessage = {
payload: {
service_domain: 'logbook',
service: 'log',
data: {
entity_id: ENTITY_IDS.heaterPumpSwitch,
name: "Heater Control System",
message: message
}
}
};
return logMessage;
}
/**
* Constructs a payload for Home Assistant service calls.
*
* @param {string} service - The service to be called (e.g., 'turn_on', 'turn_off').
* @param {string} domain - The domain of the entity (e.g., 'switch', 'light').
* @param {string} entity_id - The id of the entity to be acted upon.
* @param {number} delay - The delay in seconds before the action is performed.
* @param {object} data - Any additional data to be passed along with the service call.
* @returns {object} - The constructed payload.
*/
function buildPayload(service, domain, entity_id, delay = null, data = {}) {
let payload = {
service: service,
domain: domain,
entity_id: entity_id,
data: data
};
let message = {
payload: payload
};
if (delay !== null) {
message.delay = delay * 1000; // Convert seconds to milliseconds
}
return message;
}
function checkLightsOnTime() {
let currentTime = new Date();
let currentMinutes = currentTime.getHours() * 60 + currentTime.getMinutes();
// Convert lightOnTime and darkHours to minutes since midnight
let [onHours, onMinutes] = lightOnTime.split(':').map(Number);
let lightOnMinutes = onHours * 60 + onMinutes;
let darkMinutes = darkHours * 60;
// Calculate light off time in minutes since midnight
let lightOffMinutes = lightOnMinutes + darkMinutes;
// Adjust light off time for crossing midnight
lightOffMinutes = lightOffMinutes % 1440;
// Check if current time is within the lights on period
if (lightOnMinutes <= lightOffMinutes) {
// Scenario where light on and off times are within the same day
return currentMinutes >= lightOnMinutes && currentMinutes < lightOffMinutes;
} else {
// Scenario where light on time is today and off time is tomorrow
return currentMinutes >= lightOnMinutes || currentMinutes < lightOffMinutes;
}
}
function decideHeaterAction(currentTemp, desiredTemp, heaterState) {
if (currentTemp < desiredTemp - delta && heaterState === 'off') {
return 'turn_on';
} else if (currentTemp >= desiredTemp && heaterState === 'on') {
return 'turn_off';
}
return null;
}
function generateLogMessage(type, details) {
if (type === 'error') {
return `ERROR: ${details} is null`;
} else if (type === 'action') {
const { action, currentTemp, desiredTemp } = details;
if (action === 'turn_on') {
return `Heater Turned On - Room Temp: ${currentTemp}, Desired Temp: ${desiredTemp}`;
} else if (action === 'turn_off') {
return `Heater Turned Off - Room Temp: ${currentTemp}, Desired Temp: ${desiredTemp}`;
}
}
return 'No action required';
}
function processControlFlow() {
const nullStates = checkForNullStates();
let logOutput = null;
if (nullStates.length > 0) {
nullStates.forEach(state => {
let errorMessage = generateLogMessage('error', state);
logOutput = logbookMsg(errorMessage);
let persistentError = buildPayload('create', 'persistent_notification', '', null, { message: errorMessage, title: 'Heating System Error'});
node.send([null, persistentError]);
});
return null;
}
let desiredTemp = checkLightsOnTime() ? minLightsOnTemp : minLightsOffTemp;
let action = decideHeaterAction(roomTempSensor, desiredTemp, heaterPumpSwitch);
if (action) {
let actionDetails = { action, currentTemp: roomTempSensor, desiredTemp };
let actionMessage = generateLogMessage('action', actionDetails);
logOutput = logbookMsg(actionMessage);
let operateHeater = buildPayload(action, 'switch', ENTITY_IDS.heaterPumpSwitch);
node.send([operateHeater, logOutput]);
}
}
processControlFlow();
logDebugData();