diff --git a/climate/emma-bedroom/meltdown-center.js b/climate/emma-bedroom/meltdown-center.js new file mode 100644 index 0000000..79f549b --- /dev/null +++ b/climate/emma-bedroom/meltdown-center.js @@ -0,0 +1,72 @@ +const states = global.get('homeassistant.homeAssistant.states') +const toggle = msg.payload +const ac = global.get('emmaBedroom.aircon.installed', "diskCon") +const lastMode = flow.get("emmaBedroom.lastMode", "diskCon") +const bedTemp = states['input_number.emma_bedroom_bedtime_temp'].state + +let setTemp = bedTemp +let setEco = 'none' +let setHvac = 'cool' +let setAcFan = 'High' + +let sendHvac = { + "payload": { + "action": "climate.set_hvac_mode", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "hvac_mode": setHvac + } + } +} + +let sendTemp = { + "payload": { + "action": "climate.set_temperature", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "temperature": setTemp + } + } +} + +let sendEco = { + "payload": { + "action": "climate.set_preset_mode", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "preset_mode": setEco + } + } +} + +let sendAcFan = { + "payload": { + "action": "climate.set_fan_mode", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "fan_mode": setAcFan + } + } +} + +let reset = { + "topic": "Reset" +} + +if (toggle === 'on') { + node.status({fill:"red",shape:"dot",text:"DANGER MODE ACTIVE"}) + if (ac === 'on') { + node.send([[sendHvac,sendTemp,sendEco,sendTemp],null]) + } +} else { + node.status({fill:"green",shape:"dot",text:"Danger Mode Off"}) + node.send([null,reset]) +} \ No newline at end of file diff --git a/climate/emma-bedroom/notifications.js b/climate/emma-bedroom/notifications.js new file mode 100644 index 0000000..497d089 --- /dev/null +++ b/climate/emma-bedroom/notifications.js @@ -0,0 +1,25 @@ +const topic = msg.topic +const nightTemp = msg.nighttemp +const acMode = msg.acmode + +let coolMsg = { + "payload": `Emma bedroom AC temp has been set to ${nightTemp} °F as scheduled`, + "topic": 'Emma AC Mode: Cooling' +} +let fanOnlyMsg = { + "payload": 'Mildly chilly outside, AC running fan only.', + "topic": "Emma AC Mode: Fan Only" +} + +if (topic === 'emmabedroom-cooling') { + if (acMode === 'AC') { + node.status({fill:"green",shape:"dot",text:"AC Cooling"}) + return coolMsg + } else if (acMode === 'Fan') { + node.status({fill:"green",shape:"dot",text:"AC Fan Only"}) + return fanOnlyMsg + } +} else { + node.status({fill:"red",shape:"ring",text:"Notification not sent"}) + return null +} \ No newline at end of file diff --git a/climate/emma-bedroom/processing.js b/climate/emma-bedroom/processing.js new file mode 100644 index 0000000..eb88c80 --- /dev/null +++ b/climate/emma-bedroom/processing.js @@ -0,0 +1,369 @@ +node.log("Emma Bedroom Climate: Processing Started"); +// pull in the necessary information + +const states = global.get('homeassistant.homeAssistant.states') +const allowed = states['input_boolean.emma_bedroom_climate_protocol'].state +const ac = global.get('emmaBedroom.aircon.installed', "diskCon") +const temp = global.get("outdoorTemp.tempStr") +const payload = msg.payload +const vacation = states['input_boolean.vacation_mode'].state +const dayTemp = states['input_number.emma_bedroom_daytime_temp'].state +const nightTemp = states['input_number.emma_bedroom_night_temp'].state +const bedTemp = states['input_number.emma_bedroom_bedtime_temp'].state +const acMode = states['input_select.scheduled_climate_mode_emma_bedroom'].state +const sleeping = states['input_boolean.emma_sleeping'].state +const hotDay = states['input_boolean.hot_day'].state +const heatWarning = states["binary_sensor.heat_warning"].state +const earlyNight = states["binary_sensor.early_night_mode"].state +const danger = states['binary_sensor.heat_warning'].attributes.danger +const meltdown = states['input_boolean.meltdown_protocol'].state +const coolingActive = states['input_boolean.emma_bedroom_cooling_on'].state +node.log("Emma Bedroom Climate: Constants Set") + +// Helper function to convert a string to title case + +function convertToTitleCase(str) { + if (!str) { + return ""; + } + return str.toLowerCase().replace(/\b\w/g, (s) => s.toUpperCase()); +} + +// init variables + +let setTemp = [] +let setEco = [] +let setHvac = [] +let setCool = [] +let setSleep = [] +let setPeople = [] +let setDisplay = [] +let time = [] +let type = msg.type +let topic = msg.topic +let isWakeup = context.get("isWakeup") +node.log("Emma Bedroom Climate: Variables Defined") + +// Sleep Switch Handling +if (type === 'sleep' && payload === 'off') { + setDisplay = 'turn_on' + setPeople = 'turn_on' + if (coolingActive === 'on') { + time = 'night' + } else { + time = 'day' + } +} else if (type === 'sleep' && payload === 'on') { + setDisplay = 'turn_off' + time = 'bedtime' +} else { + time = msg.time +} + +if (topic === 'emmabedroom-wakeup') { + setSleep = 'turn_off' +} + +// Day Time +if (time === 'day') { + if (type === 'auto') { + setCool = 'turn_off' + } + if (earlyNight === 'off') { + if (ac === 'on') { + if (danger === 'Extreme') { + setTemp = nightTemp + setEco = "eco" + setHvac = "cool" + } else if (hotDay === 'on' || heatWarning === 'on') { + setTemp = dayTemp + setEco = "eco" + setHvac = "cool" + } else { + setTemp = nightTemp + setEco = "eco" + setHvac = "off" + } + } + } else if (earlyNight === 'on') { + if (ac === 'on') { + if (danger === 'Extreme') { + setTemp === bedTemp + } else { + setTemp = nightTemp + } + if (acMode === 'AC') { + setHvac = 'cool' + } else if (acMode === 'fan') { + setHvac = 'fan_only' + } else { + setHvac = 'off' + } + if (hotDay === 'on') { + setEco = 'none' + } else { + setEco = 'eco' + } + } + } +// Night Time +} else if (time === 'night') { + // If this is being run at scheduled time, turn on input_boolean.emma_bedroom_cooling_on + if (type === 'auto') { + setCool = 'turn_on' + } + // Decide temperature + if (type === 'sleep' && payload === 'off') { + setTemp = dayTemp + } else if (danger === 'Extreme') { + setTemp = bedTemp + } else { + setTemp = nightTemp + } + // Decide eco mode + if (sleeping === 'on') { + setEco = 'none' + } else { + setEco = 'eco' + } + // Decide HVAC mode + if (acMode === 'AC') { + setHvac = 'cool' + } else if (acMode === 'Fan') { + setHvac = 'fan_only' + } else { + setHvac = 'off' + } +// Bed Time +} else if (time === 'bedtime') { + setPeople = 'turn_off' + if (ac === 'on') { + setTemp = bedTemp + setEco = 'none' + if (acMode === 'AC') { + setHvac = 'cool' + } else if (acMode === 'Fan') { + setHvac = "fan_only" + } else { + setHvac = "off" + } + } +} +node.log("Emma Bedroom Climate: Decision Logic Complete") + +// Define message payloads + +let sendCool = { + "payload": { + "action": `input_boolean.${setCool}`, + "target": { + "entity_id": ["input_boolean.emma_bedroom_cooling_on"] + }, + "data": {} + } +} + +let sendSleep = { + "payload": { + "action": `input_boolean.${setSleep}`, + "target": { + "entity_id": ["input_boolean.emma_sleeping"] + }, + "data": {} + } +} + +let sendPeople = { + "payload": { + "action": `input_boolean.${setPeople}`, + "target": { + "entity_id": ["input_boolean.emma_awake"] + }, + "data": {} + } +} + +let sendDisplay = { + "payload": { + "action": `switch.${setDisplay}`, + "target": { + "entity_id": ["switch.emma_bedroom_aircon_display"] + }, + "data": {} + } +} + +let notify = { + "topic": topic, + "nighttemp": nightTemp, + "acmode": acMode +} + +let sendHvac = { + "payload": { + "action": "climate.set_hvac_mode", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "hvac_mode": setHvac + } + } +} + +let sendTemp = { + "payload": { + "action": "climate.set_temperature", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "temperature": setTemp + } + } +} + +let sendEco = { + "payload": { + "action": "climate.set_preset_mode", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "preset_mode": setEco + } + } +} + +let sendAcFan = { + "payload": { + "action": "climate.set_fan_mode", + "target": { + "entity_id": ["climate.emma_bedroom_aircon"] + }, + "data": { + "fan_mode": "auto" + } + } +} + +node.log("Emma Bedroom Climate: Message Payloads Defined") + +// Log the parameters that were chosen, for debugging purposes + +node.log("----- Emma Bedroom Climate: Set Parameters -----") +node.log(`setTemp: ${setTemp}`) +node.log(`setEco: ${setEco}`) +node.log(`setHvac: ${setHvac}`) +node.log(`setCool: ${setCool}`) +node.log(`setSleep: ${setSleep}`) +node.log(`setPeople: ${setPeople}`) +node.log(`setDisplay: ${setDisplay}`) +node.log(`time: ${time}`) +node.log(`type: ${type}`) +node.log(`topic: ${topic}`) +node.log("----- Emma Bedroom Climate: End Parameters -----") + +// If this was an automated trigger, set the cooling context for the bedroom accordingly. + +if (type === 'auto' && time != 'bedtime') { + node.send([null, sendCool, null]) + node.log("Emma Bedroom Climate: Cooling Context Set") +} + +if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'off') { + node.log("Emma Bedroom Climate: Auto") + if (sleeping === 'on' && topic != 'emmabedroom-wakeup') { + node.status({ fill: "red", shape: "ring", text: "Blocked (sleep mode)" }) + node.log("Emma Bedroom Climate: Blocked (sleep mode)") + } else { + if (topic === 'emmabedroom-cooling' && ac === 'on') { + node.status({ fill: "green", shape: "dot", text: "Cooling Schedule" }) + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Auto/Cooling") + } else if (topic === 'emmabedroom-bedtime') { + node.send([null, sendPeople, null]) + node.status({ fill: "green", shape: "dot", text: "Bedtime" }) + node.log("Emma Bedroom Climate: Auto/Bedtime") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Auto/Bedtime/AC") + } + } else if (topic === 'emmabedroom-wakeup') { + node.status({ fill: "green", shape: "dot", text: "Wakeup Schedule" }) + node.log("Emma Bedroom Climate: Auto/Wakeup") + if (sleeping === 'off') { + context.set("isWakeup", false) + node.log("Emma Bedroom Climate: Auto/Wakeup/Sleep Off") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Auto/Wakeup/AC On") + } + } else if (sleeping === 'on') { + context.set("isWakeup", true) + node.send([null, sendSleep, null]) + node.log("Emma Bedroom Climate: Auto/Wakeup/Sleep On") + } + } + } +// Manual Responses +} else if (type === 'manual') { + node.log("Emma Bedroom Climate: Manual") + if (time === 'night') { + node.status({ fill: "blue", shape: "dot", text: "Manual Night" }) + node.log("Emma Bedroom Climate: Manual/Night") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Manual/Night/AC") + } + } else if (time === 'day') { + node.status({ fill: "blue", shape: "dot", text: "Manual Day" }) + node.log("Emma Bedroom Climate: Manual/Day") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Manual/Day/AC") + } + } else if (time === 'bedtime') { + node.status({ fill: "blue", shape: "dot", text: "Manual Bedtime" }) + node.log("Emma Bedroom Climate: Manual/Bedtime") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Manual/Bedtime/AC") + } + } + context.set("isWakeup", false) +// Sleep Switch Responses +} else if (type === 'sleep') { + if (payload === 'off') { + node.send([null, sendPeople, null]) + } + node.log("Emma Bedroom Climate: Sleep") + if (time === 'bedtime') { + node.status({ fill: "blue", shape: "dot", text: "Sleep" }) + node.send([null, sendPeople, null]) + node.log("Emma Bedroom Climate: Sleep/Bedtime") + if (ac === 'on') { + node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Emma Bedroom Climate: Sleep/Bedtime/AC") + } + } else { + node.status({ fill: "blue", shape: "dot", text: "Wakeup" }) + node.log("Emma Bedroom Climate: Sleep/Day") + if (ac === 'on') { + node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log(`Emma Bedroom Climate: Sleep/${convertToTitleCase(time)}/AC`) + } + } + context.set("isWakeup", false) +} else if (meltdown === 'on') { + node.status({ fill: "red", shape: "ring", text: "Blocked (Meltdown Protocol)" }) + node.log("Emma Bedroom Climate: Blocked (Meltdown Protocol)") +} else if (vacation === 'on') { + node.status({ fill: "red", shape: "ring", text: "Blocked (Vacation Mode)" }) + node.log("Emma Bedroom Climate: Blocked (Vacation Mode)") +} else { + node.status({ fill: "red", shape: "ring", text: "Blocked (Automation Disabled)" }) + node.log("Emma Bedroom Climate: Blocked (Automation Disabled)") +} + +node.log("Emma Bedroom Climate: Processing Complete") diff --git a/climate/emma-bedroom/reset.js b/climate/emma-bedroom/reset.js new file mode 100644 index 0000000..4597a37 --- /dev/null +++ b/climate/emma-bedroom/reset.js @@ -0,0 +1,30 @@ +const lastMode = flow.get("emmaBedroom.lastMode", "diskCon") +const linkSource = msg._linkSource + +let setTime = [] +let setType = [] +let setTopic = [] + +if (lastMode === 'day') { + setTime = 'day' + setType = 'manual' + setTopic = 'manual-day' +} else if (lastMode === 'night') { + setTime = 'night' + setType = 'manual' + setTopic = 'manual-night' +} else if (lastMode === 'bedtime') { + setTime = 'bedtime' + setType = 'manual' + setTopic = 'manual-bedtime' +} + +let reset = { + "type": setType, + "time": setTime, + "topic": setTopic, + "_linkSource": linkSource +} + +node.send(reset) +node.status({ fill: "green", shape: "dot", text: `Mode reset to ${setTopic}`}) \ No newline at end of file diff --git a/climate/emma-bedroom/set-mode.js b/climate/emma-bedroom/set-mode.js new file mode 100644 index 0000000..aad72ee --- /dev/null +++ b/climate/emma-bedroom/set-mode.js @@ -0,0 +1,20 @@ +const states = global.get("homeassistant.homeAssistant.states") +const sleeping = states["input_boolean.emma_sleeping"].state +const earlyNight = states["binary_sensor.early_night_mode"].state +const giveMeDarkness = states["input_boolean.give_me_darkness"].state +const nightMode = states["input_boolean.night_mode"].state +const goodnight = states["input_boolean.goodnight"].state +const coolingActive = states['input_boolean.emma_bedroom_cooling_on'].state + +let lastMode = [] + +if (sleeping === 'on' || nightMode === 'on' || goodnight === 'on') { + lastMode = 'bedtime' +} else if (earlyNight === 'on' || giveMeDarkness === 'on' || coolingActive === 'on') { + lastMode = 'night' +} else { + lastMode = 'day' +} + +flow.set("emmaBedroom.lastMode", lastMode, "diskCon") +node.status({ fill: "green", shape: "dot", text: `Last Mode Set: ${lastMode}`}) \ No newline at end of file