From 25780c8c5d8bfe5079793f2d4a50303f534c7854 Mon Sep 17 00:00:00 2001 From: Tony Stork Date: Thu, 28 Sep 2023 12:28:27 -0400 Subject: [PATCH] Initial commit --- climate/kallen/old/processing_old.js | 48 +++ climate/kallen/old/schedmode_old.js | 16 + climate/kallen/prefilter.js | 10 + climate/kallen/processing.js | 134 +++++++ climate/kallen/schedmode.js | 16 + climate/master-bedroom/meltdown-center.js | 89 +++++ climate/master-bedroom/notifications.js | 33 ++ climate/master-bedroom/processing.js | 405 ++++++++++++++++++++++ climate/master-bedroom/reset.js | 30 ++ climate/master-bedroom/set-mode.js | 20 ++ front-porch/check-parameters.js | 26 ++ front-porch/delay.js | 13 + front-porch/if-motion.js | 12 + front-porch/shut-off.js | 20 ++ jsconfig.json | 10 + laundry/processing-start.js | 17 + laundry/processing.js | 153 ++++++++ laundry/timer-change.js | 40 +++ lightfx/lightning-processing.js | 63 ++++ lightfx/processing.js | 1 + sports/events-today.js | 34 ++ sports/notes.json | 10 + sports/scores.js | 24 ++ time-based/old/early-night.js | 11 + time-based/old/holiday-day.js | 20 ++ time-based/old/holiday-night.js | 13 + time-based/old/sunset-lights.js | 18 + time-based/old/work-processing.js | 26 ++ time-based/processing-start.js | 87 +++++ time-based/processing.js | 378 ++++++++++++++++++++ 30 files changed, 1777 insertions(+) create mode 100644 climate/kallen/old/processing_old.js create mode 100644 climate/kallen/old/schedmode_old.js create mode 100644 climate/kallen/prefilter.js create mode 100644 climate/kallen/processing.js create mode 100644 climate/kallen/schedmode.js create mode 100644 climate/master-bedroom/meltdown-center.js create mode 100644 climate/master-bedroom/notifications.js create mode 100644 climate/master-bedroom/processing.js create mode 100644 climate/master-bedroom/reset.js create mode 100644 climate/master-bedroom/set-mode.js create mode 100644 front-porch/check-parameters.js create mode 100644 front-porch/delay.js create mode 100644 front-porch/if-motion.js create mode 100644 front-porch/shut-off.js create mode 100644 jsconfig.json create mode 100644 laundry/processing-start.js create mode 100644 laundry/processing.js create mode 100644 laundry/timer-change.js create mode 100644 lightfx/lightning-processing.js create mode 100644 lightfx/processing.js create mode 100644 sports/events-today.js create mode 100644 sports/notes.json create mode 100644 sports/scores.js create mode 100644 time-based/old/early-night.js create mode 100644 time-based/old/holiday-day.js create mode 100644 time-based/old/holiday-night.js create mode 100644 time-based/old/sunset-lights.js create mode 100644 time-based/old/work-processing.js create mode 100644 time-based/processing-start.js create mode 100644 time-based/processing.js diff --git a/climate/kallen/old/processing_old.js b/climate/kallen/old/processing_old.js new file mode 100644 index 0000000..f3a1d2e --- /dev/null +++ b/climate/kallen/old/processing_old.js @@ -0,0 +1,48 @@ +const states = global.get('homeassistant.homeAssistant.states') +const vacationMode = states['input_boolean.vacation_mode'].state +const overnight = states['input_boolean.kallen_overnight'].state +const kallenLoc = states['person.kallen_stork'].state +const nightVolume = states['input_number.kallen_bedroom_google_speaker_night_volume'].state +const brightness = states['switch.adaptive_lighting_kallen_bedroom'].attributes.brightness_pct +const fadeDay = states['input_number.wakeup_lights_fade_day'].state +const fadeNight = states['input_number.wakeup_lights_fade_night'].state +const hotDay = states['input_boolean.hot_day'].state +const heatWarning = states['binary_sensor.heat_warning'].state +const fanSeparate = states['binary_sensor.kallen_fan_separate_schedule'].state +const topic = msg.topic +const toggle = msg.toggle + +let fan = [] + +if (hotDay === 'on' || heatWarning === 'on') { + fan = 'on' +} else { + fan = 'off' +} + +if (vacationMode === 'off' && overnight === 'off' && kallenLoc === 'home') { + if (topic === 'kallen-fan' && fanSeparate === 'on') { + node.status({fill:"green",shape:"dot",text:"Fan"}); + return[null,null,null,msg] + } else if (toggle === 'off') { + msg.brightness = brightness + msg.fade_day = fadeDay * 60 + msg.fade_night = fadeNight * 60 + msg.fan = fan + node.status({fill:"green",shape:"dot",text:"Wakeup"}); + return[null,msg,null,null] + } else if (toggle === 'on') { + msg.volume = nightVolume + node.status({fill:"green",shape:"dot",text:"Sleep"}); + return[null,null,msg,null] + } +} else { + if (topic === 'boolean') { + msg.toggle = 'off' + node.status({fill:"red",shape:"ring",text:"Blocked"}); + return [msg,null,null,null] + } else { + node.status({fill:"red",shape:"ring",text:"Blocked"}); + return null + } +} \ No newline at end of file diff --git a/climate/kallen/old/schedmode_old.js b/climate/kallen/old/schedmode_old.js new file mode 100644 index 0000000..f65ba9e --- /dev/null +++ b/climate/kallen/old/schedmode_old.js @@ -0,0 +1,16 @@ +var states = global.get('homeassistant.homeAssistant.states') +var schedMode = states['input_select.scheduled_climate_mode_kallen_fan'].state + +msg.topic = 'common' +msg.voice = 'Joanna' + +if (schedMode === 'White Noise') { + node.status({fill:"green",shape:"dot",text:"White Noise"}); + return[msg,null,null] +} else if (schedMode === 'Fan') { + node.status({fill:"green",shape:"dot",text:"Fan"}); + return[null,msg,null] +} else { + node.status({fill:"blue",shape:"dot",text:"N/A"}); + return[null,null,msg] +} diff --git a/climate/kallen/prefilter.js b/climate/kallen/prefilter.js new file mode 100644 index 0000000..7f5ba1f --- /dev/null +++ b/climate/kallen/prefilter.js @@ -0,0 +1,10 @@ +const states = global.get('homeassistant.homeAssistant.states') +const vacationMode = states['input_boolean.vacation_mode'].state +const overnight = states['input_boolean.kallen_overnight'].state +const kallenLoc = states['person.kallen_stork'].state + +if (vacationMode === 'off' && overnight === 'off' && kallenLoc === 'home') { + node.status({fill:"green",shape:"dot",text:"Proceed"}) + node.log("Kallen climate flow allowed to proceed") + node.send(msg) +} \ No newline at end of file diff --git a/climate/kallen/processing.js b/climate/kallen/processing.js new file mode 100644 index 0000000..b5bc04d --- /dev/null +++ b/climate/kallen/processing.js @@ -0,0 +1,134 @@ +const states = global.get('homeassistant.homeAssistant.states') +const vacationMode = states['input_boolean.vacation_mode'].state +const overnight = states['input_boolean.kallen_overnight'].state +const kallenLoc = states['person.kallen_stork'].state +const dayVolume = states['input_number.kallen_bedroom_google_speaker_day_volume'].state +const nightVolume = states['input_number.kallen_bedroom_google_speaker_night_volume'].state +const brightness = states['switch.adaptive_lighting_kallen_bedroom'].attributes.brightness_pct +const fadeNight = states['input_number.wakeup_lights_fade_night'].state +const hotDay = states['input_boolean.hot_day'].state +const heatWarning = states['binary_sensor.heat_warning'].state +const fanSeparate = states['binary_sensor.kallen_fan_separate_schedule'].state +const schedMode = states['input_select.scheduled_climate_mode_kallen_fan'].state +const topic = msg.topic +const toggle = msg.toggle +node.log("Kallen Bedroom: Constants Set") + +let setFan = [] +let setWhiteNoise = [] +let setVolume = [] +let setLights = [] +node.log("Kallen Bedroom: Variables Defined") + +if (toggle === 'off' && (hotDay === 'on' || heatWarning === 'on')) { + setFan = 'turn_on' +} else if ((toggle === 'on' || topic === 'kallen-fan') && schedMode === 'Fan') { + setFan = 'turn_on' +} else { + setFan = 'turn_off' +} + +if (schedMode === 'White Noise' && toggle === 'on') { + setWhiteNoise = 'turn_on' +} else { + setWhiteNoise = 'turn_off' +} + +if (toggle === 'on') { + setVolume = parseFloat(nightVolume) + setLights = 'turn_on' +} else { + setVolume = parseFloat(dayVolume) +} + +let fadeMult = fadeNight * 60 +let fadeFinal = Math.round(fadeMult) + +let brtFinal = Math.round(brightness) + +node.log("Kallen Bedroom: Decision Logic Complete") + +let sendFan = { + "payload": { + "domain": "fan", + "service": setFan, + "target": { + "entity_id": ["fan.kallen_bedroom_fan"] + }, + "data": {} + } +} + +let sendWhiteNoise = { + "payload": { + "domain": "input_boolean", + "service": setWhiteNoise, + "target": { + "entity_id": ["input_boolean.white_noise_kallen_bedroom"] + }, + "data": {} + } +} + +let sendVolume = { + "payload": { + "domain": "media_player", + "service": "set_volume", + "target": { + "entity_id": ["media_player.kallen_bedroom_google_speaker"] + }, + "data": { + "volume": setVolume + } + } +} + +let sendLights = { + "payload": { + "domain": "light", + "service": setLights, + "target": { + "entity_id": ["light.kallen_bedroom_lights"] + }, + "data": {} + } +} + +let wakeMsg = { + "brightness": brtFinal, + "fade": fadeFinal +} + +let sleepMsg = { + "payload": "sleep" +} + +node.log("Kallen Bedroom: Message Payloads Defined") + +node.log("----- Kallen Bedroom: Set Parameters -----") +node.log("setFan: " + setFan) +node.log("setWhiteNoise: " + setWhiteNoise) +node.log("setVolume: " + setVolume) +node.log("setLights: " + setLights) +node.log("----- Kallen Bedroom: End Parameters -----") + +if (vacationMode === 'off' && overnight === 'off' && kallenLoc === 'home') { + if (topic === 'kallen-fan' && fanSeparate === 'on') { + node.status({fill:"green",shape:"dot",text:"Fan"}) + node.log("Kallen Bedroom: Early Fan") + node.send([null,[sendFan,sendWhiteNoise],null]) + } else if (toggle === 'off') { + node.status({fill:"green",shape:"dot",text:"Wakeup"}) + node.log("Kallen Bedroom: Wake") + node.send([wakeMsg,[sendFan,sendWhiteNoise,sendVolume],null]) + } else if (toggle === 'on') { + node.status({fill:"green",shape:"dot",text:"Sleep"}) + node.log("Kallen Bedroom: Sleep") + node.send([null,[sendFan,sendWhiteNoise,sendVolume,sendLights],sleepMsg]) + } +} else { + node.status({fill:"red",shape:"ring",text:"Blocked"}) + node.log("Kallen Bedroom: Flow Blocked") +} + +node.log("Kallen Bedroom: Processing Complete") \ No newline at end of file diff --git a/climate/kallen/schedmode.js b/climate/kallen/schedmode.js new file mode 100644 index 0000000..f65ba9e --- /dev/null +++ b/climate/kallen/schedmode.js @@ -0,0 +1,16 @@ +var states = global.get('homeassistant.homeAssistant.states') +var schedMode = states['input_select.scheduled_climate_mode_kallen_fan'].state + +msg.topic = 'common' +msg.voice = 'Joanna' + +if (schedMode === 'White Noise') { + node.status({fill:"green",shape:"dot",text:"White Noise"}); + return[msg,null,null] +} else if (schedMode === 'Fan') { + node.status({fill:"green",shape:"dot",text:"Fan"}); + return[null,msg,null] +} else { + node.status({fill:"blue",shape:"dot",text:"N/A"}); + return[null,null,msg] +} diff --git a/climate/master-bedroom/meltdown-center.js b/climate/master-bedroom/meltdown-center.js new file mode 100644 index 0000000..3420861 --- /dev/null +++ b/climate/master-bedroom/meltdown-center.js @@ -0,0 +1,89 @@ +const states = global.get('homeassistant.homeAssistant.states') +const toggle = msg.payload +const ac = global.get('mb_aircon_installed', "diskCon") +const lastMode = flow.get("lastMode", "diskCon") +const bedTemp = states['input_number.master_bedroom_bedtime_temp'].state + +let setTemp = bedTemp +let setEco = 'none' +let setHvac = 'cool' +let setAcFan = 'High' +let setFan = 'turn_off' + +let sendFan = { + "payload": { + "domain": "fan", + "service": setFan, + "target": { + "entity_id": ["fan.master_bedroom_fan"] + }, + "data": {} + } +} + +let sendHvac = { + "payload": { + "domain": "climate", + "service": "set_hvac_mode", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "hvac_mode": setHvac + } + } +} + +let sendTemp = { + "payload": { + "domain": "climate", + "service": "set_temperature", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "temperature": setTemp + } + } +} + +let sendEco = { + "payload": { + "domain": "climate", + "service": "set_preset_mode", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "preset_mode": setEco + } + } +} + +let sendAcFan = { + "payload": { + "domain": "climate", + "service": "set_fan_mode", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "fan_mode": setAcFan + } + } +} + +let reset = { + "topic": "Reset" +} + +if (toggle === 'on') { + node.status({fill:"red",shape:"dot",text:"DANGER MODE ACTIVE"}) + node.send([null,sendFan,null]) + if (ac === 'on') { + node.send([[sendHvac,sendTemp,sendEco,sendTemp],null,null]) + } +} else { + node.status({fill:"green",shape:"dot",text:"Danger Mode Off"}) + node.send([null,null,reset]) +} \ No newline at end of file diff --git a/climate/master-bedroom/notifications.js b/climate/master-bedroom/notifications.js new file mode 100644 index 0000000..a989eb0 --- /dev/null +++ b/climate/master-bedroom/notifications.js @@ -0,0 +1,33 @@ +const topic = msg.topic +const nightTemp = msg.nighttemp +const acMode = msg.acmode +const fanMode = msg.fanmode + +let coolMsg = { + "payload": 'Master bedroom AC temp has been set to ' + nightTemp + '°F as scheduled', + "topic": 'AC Mode: Cooling' +} +let fanOnlyMsg = { + "payload": 'Too cold outside, AC running fan only.', + "topic": "AC Mode: Fan Only" +} +let fanSchedMsg = { + "payload": 'Master bedroom fan has been activated as scheduled.', + "topic": 'Fan Schedule Activated' +} + +if (topic === 'mrbedroom-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 if (topic === 'mrbedroom-fan' && fanMode === 'Fan') { + node.status({fill:"green",shape:"dot",text:"Fan Schedule"}) + return fanSchedMsg +} else { + node.status({fill:"red",shape:"ring",text:"Notification not sent"}) + return null +} \ No newline at end of file diff --git a/climate/master-bedroom/processing.js b/climate/master-bedroom/processing.js new file mode 100644 index 0000000..51fac34 --- /dev/null +++ b/climate/master-bedroom/processing.js @@ -0,0 +1,405 @@ +node.log("Master Bedroom Climate: Processing Started") +// pull in the necessary information + +const states = global.get('homeassistant.homeAssistant.states') +const allowed = states['input_boolean.master_bedroom_climate_protocol'].state +const ac = global.get('mb_aircon_installed', "diskCon") +const temp = global.get("tempStr") +const payload = msg.payload +const vacation = states['input_boolean.vacation_mode'].state +const highTemp = states['sensor.today_corrected_high_temp'].state +const dayTemp = states['input_number.master_bedroom_daytime_temp'].state +const nightTemp = states['input_number.master_bedroom_night_temp'].state +const bedTemp = states['input_number.master_bedroom_bedtime_temp'].state +const showerMode = states['input_boolean.shower_mode'].state +const nightVolume = states['input_number.master_bedroom_echo_dot_night_volume'].state +const fanMode = states['input_select.scheduled_climate_mode_master_bedroom_fan'].state +const acMode = states['input_select.scheduled_climate_mode_master_bedroom_aircon'].state +const sleeping = states['input_boolean.master_bedroom_sleeping'].state +const hotDay = states['input_boolean.hot_day'].state +const heatWarning = states["binary_sensor.heat_warning"].state +const showerCooldown = states["timer.shower_mode_cooldown"].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.master_bedroom_cooling_on'].state +const echoDotDND = 'switch.basement_echo_dot_do_not_disturb_switch' +node.log("Master Bedroom Climate: Constants Set") + +// init variables + +let setTemp = [] +let setEco = [] +let setHvac = [] +let setFan = [] +let setCool = [] +let setSleep = [] +let setDisplay = [] +let time = [] +let type = msg.type +let topic = msg.topic +let echoDotService = [] +node.log("Master Bedroom Climate: Variables Defined") + +// Sleep Switch Handling +if (type === 'sleep' && payload === 'off') { + setDisplay = 'turn_on' + echoDotService = 'turn_off' + if (coolingActive === 'on') { + time = 'night' + } else { + time = 'day' + } +} else if (type === 'sleep' && payload === 'on') { + setDisplay = 'turn_off' + echoDotService = 'turn_on' + time = 'bedtime' +} else { + time = msg.time +} + +if (topic === 'mrbedroom-wakeup') { + setSleep = 'turn_off' +} + +// Day Time +if (time === 'day') { + if (type === 'auto') { + setCool = 'turn_off' + } + if (earlyNight === 'off') { + setFan = "turn_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 (fanMode === 'Fan') { + setFan = 'turn_on' + } else { + setFan = 'turn_off' + } + if (acMode === 'AC') { + setHvac = 'cool' + } else if (acMode === 'fan') { + setHvac = 'fan_only' + } else { + setHvac = 'off' + } + if (hotDay === 'on') { + setEco = 'off' + } else { + setEco = 'on' + } + } + } +// Night Time +} else if (time === 'night') { + if (type === 'auto') { + setCool = 'turn_on' + } + if (danger === 'Extreme') { + setTemp = bedTemp + } else { + setTemp = nightTemp + } + if (hotDay === 'on' || sleeping === 'on') { + setEco = 'none' + } else { + setEco = 'eco' + } + if (acMode === 'AC') { + setHvac = 'cool' + } else if (acMode === 'Fan') { + setHvac = 'fan_only' + } else { + setHvac = 'off' + } + if (type === 'sleep' && payload === 'off') { + setFan = 'turn_off' + } else if (fanMode === 'Fan') { + setFan = 'turn_on' + } +// Bed Time +} else if (time === 'bedtime') { + if (ac === 'on') { + setTemp = bedTemp + setEco = 'none' + if (acMode === 'AC') { + setHvac = 'cool' + } else if (acMode === 'Fan') { + setHvac = "fan_only" + } + } + if (fanMode === 'Fan') { + setFan = 'turn_on' + } +} +node.log("Master Bedroom Climate: Decision Logic Complete") + +// Define message payloads + +let sendFan = { + "payload": { + "domain": "fan", + "service": setFan, + "target": { + "entity_id": ["fan.master_bedroom_fan"] + }, + "data": {} + } +} + +let sendCool = { + "payload": { + "domain": "input_boolean", + "service": setCool, + "target": { + "entity_id": ["input_boolean.master_bedroom_cooling_on"] + }, + "data": {} + } +} + +let sendSleep = { + "payload": { + "domain": "input_boolean", + "service": setSleep, + "target": { + "entity_id": ["input_boolean.master_bedroom_sleeping"] + }, + "data": {} + } +} + +let sendDisplay = { + "payload": { + "domain": "switch", + "service": setDisplay, + "target": { + "entity_id": ["switch.master_bedroom_aircon_display"] + }, + "data": {} + } +} + +let notify = { + "topic": topic, + "nighttemp": nightTemp, + "acmode": acMode, + "fanmode": fanMode +} + +let sendHvac = { + "payload": { + "domain": "climate", + "service": "set_hvac_mode", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "hvac_mode": setHvac + } + } +} + +let sendTemp = { + "payload": { + "domain": "climate", + "service": "set_temperature", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "temperature": setTemp + } + } +} + +let sendEco = { + "payload": { + "domain": "climate", + "service": "set_preset_mode", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "preset_mode": setEco + } + } +} + +let sendAcFan = { + "payload": { + "domain": "climate", + "service": "set_fan_mode", + "target": { + "entity_id": ["climate.master_bedroom_aircon"] + }, + "data": { + "fan_mode": "Auto" + } + } +} + +let sendEchoDotDND = { + "payload": { + "domain": "switch", + "service": echoDotService, + "target": { + "entity_id": ["switch.basement_echo_dot_do_not_disturb_switch"] + }, + "data": {} + } +} + +node.log("Master Bedroom Climate: Message Payloads Defined") + +// Log the parameters that were chosen, for debugging purposes + +node.log("----- Master Bedroom Climate: Set Parameters -----") +node.log("setTemp: " + setTemp) +node.log("setEco: " + setEco) +node.log("setHvac: " + setHvac) +node.log("setFan: " + setFan) +node.log("setCool: " + setCool) +node.log("setSleep: " + setSleep) +node.log("setDisplay " + setDisplay) +node.log("time: " + time) +node.log("type: " + type) +node.log("topic: " + topic) +node.log("----- Master 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, null, sendCool, null]) + node.log("Master Bedroom Climate: Cooling Context Set") +} + +// Automated responses +if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'off') { + node.log("Master Bedroom Climate: Auto") + if (sleeping === 'on' && topic != 'mrbedroom-wakeup') { + node.status({ fill: "red", shape: "ring", text: "Blocked (sleep mode)" }) + node.log("Master Bedroom Climate: Blocked (sleep mode)") + } else { + if (topic === 'mrbedroom-cooling' && ac === 'on') { + node.status({ fill: "green", shape: "dot", text: "Cooling Schedule" }) + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null]) + node.log("Master Bedroom Climate: Auto/Cooling") + } else if (topic === 'mrbedroom-bedtime') { + node.status({ fill: "green", shape: "dot", text: "Bedtime" }) + node.log("Master Bedroom Climate: Auto/Bedtime") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Auto/Bedtime/AC") + } + if (fanMode === 'fan') { + node.send([null, sendFan, null, null]) + node.log("Master Bedroom Climate: Auto/Bedtime/Fan") + } + } else if (topic === 'mrbedroom-fan' && fanMode === 'Fan') { + node.status({ fill: "green", shape: "dot", text: "Fan Schedule" }) + node.send([null, sendFan, null, null]) + node.log("Master Bedroom Climate: Auto/Fan") + } else if (topic === 'mrbedroom-wakeup') { + node.status({ fill: "green", shape: "dot", text: "Wakeup Schedule" }) + node.log("Master Bedroom Climate: Auto/Wakeup") + if (sleeping === 'off') { + node.send([null, sendFan, null, null]) + node.log("Master Bedroom Climate: Auto/Wakeup/Sleep Off") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Auto/Wakeup/AC On") + } + } else if (sleeping === 'on') { + node.send([null, null, sendSleep, null]) + node.log("Master Bedroom Climate: Auto/Wakeup/Sleep On") + } + } + } +// Manual Responses +} else if (type === 'manual') { + node.log("Master Bedroom Climate: Manual") + if (time === 'night') { + node.status({ fill: "blue", shape: "dot", text: "Manual Night" }) + node.send([null, sendFan, null, null]) + node.log("Master Bedroom Climate: Manual/Night") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Manual/Night/AC") + } + } else if (time === 'day') { + node.status({ fill: "blue", shape: "dot", text: "Manual Day" }) + node.send([null, sendFan, null, null]) + node.log("Master Bedroom Climate: Manual/Day") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Manual/Day/AC") + } + } else if (time === 'bedtime') { + node.status({ fill: "blue", shape: "dot", text: "Manual Bedtime" }) + node.send([null, sendFan, null, null]) + node.log("Master Bedroom Climate: Manual/Bedtime") + if (ac === 'on') { + node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Manual/Bedtime/AC") + } + } +} else if (type === 'sleep') { + node.log("Master Bedroom Climate: Sleep") + if (time === 'night') { + node.status({ fill: "blue", shape: "dot", text: "Wakeup (Hot Day)" }) + node.send([null, sendFan, sendEchoDotDND, null]) + node.log("Master Bedroom Climate: Sleep/Night") + if (ac === 'on') { + node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Sleep/Night/AC") + } + } else if (time === 'day') { + node.status({ fill: "blue", shape: "dot", text: "Wakeup" }) + node.send([null, sendFan, sendEchoDotDND, null]) + node.log("Master Bedroom Climate: Sleep/Day") + if (ac === 'on') { + node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Sleep/Day/AC") + } + } else if (time === 'bedtime') { + node.status({ fill: "blue", shape: "dot", text: "Sleep" }) + node.send([null, sendFan, sendEchoDotDND, null]) + node.log("Master Bedroom Climate: Sleep/Bedtime") + if (ac === 'on') { + node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null, null]) + node.log("Master Bedroom Climate: Sleep/Bedtime/AC") + } + } +} else if (meltdown === 'on') { + node.status({ fill: "red", shape: "ring", text: "Blocked (Meltdown Protocol)" }) + node.log("Master Bedroom Climate: Blocked (Meltdown Protocol)") +} else if (vacation === 'on') { + node.status({ fill: "red", shape: "ring", text: "Blocked (Vacation Mode)" }) + node.log("Master Bedroom Climate: Blocked (Vacation Mode)") +} else { + node.status({ fill: "red", shape: "ring", text: "Blocked (Automation Disabled)" }) + node.log("Master Bedroom Climate: Blocked (Automation Disabled)") +} + +node.log("Master Bedroom Climate: Processing Complete") diff --git a/climate/master-bedroom/reset.js b/climate/master-bedroom/reset.js new file mode 100644 index 0000000..39b0018 --- /dev/null +++ b/climate/master-bedroom/reset.js @@ -0,0 +1,30 @@ +const lastMode = flow.get('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/master-bedroom/set-mode.js b/climate/master-bedroom/set-mode.js new file mode 100644 index 0000000..54bda42 --- /dev/null +++ b/climate/master-bedroom/set-mode.js @@ -0,0 +1,20 @@ +const states = global.get("homeassistant.homeAssistant.states") +const sleeping = states["input_boolean.master_bedroom_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.master_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("lastMode", lastMode, "diskCon") +node.status({ fill: "green", shape: "dot", text: "Last Mode Set: " + lastMode}) \ No newline at end of file diff --git a/front-porch/check-parameters.js b/front-porch/check-parameters.js new file mode 100644 index 0000000..a3130f8 --- /dev/null +++ b/front-porch/check-parameters.js @@ -0,0 +1,26 @@ +const states = global.get('homeassistant.homeAssistant.states') +const earlyNightMode = states['binary_sensor.early_night_mode'].state +const frontPorchToggle = states['input_boolean.front_porch_light_on'].state +const holidaymode = states['input_boolean.holiday_mode'].state +const currentholiday = states['input_select.holiday_animation'].state +const holiday = currentholiday.toLowerCase() + +let holidayMsg = { + "holiday": holiday +} + +if (earlyNightMode === 'on' && frontPorchToggle === 'on') { + if (holidaymode === 'on') { + node.status({fill:"blue",shape:"dot",text:"Holiday On"}) + node.send([holidayMsg,msg]) + } else { + node.status({fill:"green",shape:"dot",text:"Holiday Off"}) + node.send([null,msg]) + } +} else { + if (frontPorchToggle === 'off') { + node.status({ fill: "red", shape: "ring", text: "Disabled" }) + } else if (earlyNightMode === 'off') { + node.status({ fill: "red", shape: "ring", text: "Daytime" }) + } +} \ No newline at end of file diff --git a/front-porch/delay.js b/front-porch/delay.js new file mode 100644 index 0000000..9c0b17f --- /dev/null +++ b/front-porch/delay.js @@ -0,0 +1,13 @@ +const states = global.get('homeassistant.homeAssistant.states') +const delay = states['input_number.front_porch_motion_off_delay'].state +const duration = delay * 60 +const lights = states['light.front_porch_light'].state + +msg.duration = duration + +if (lights === 'on') { + node.status({fill:"green",shape:"dot",text: parseInt(delay) + " minutes"}) + node.send(msg) +} else { + node.status({fill:"red",shape:"ring",text:"Light Off"}) +} \ No newline at end of file diff --git a/front-porch/if-motion.js b/front-porch/if-motion.js new file mode 100644 index 0000000..397b8d4 --- /dev/null +++ b/front-porch/if-motion.js @@ -0,0 +1,12 @@ +const states = global.get('homeassistant.homeAssistant.states') +const deliveryMode = states['input_boolean.delivery_mode'].state +const detect = msg.detect + +node.send([null,msg]) + +if (detect === 'motion' && deliveryMode === 'on') { + node.status({fill:"green",shape:"dot",text:"Motion/Delivery"}) + node.send([msg,null]) +} else { + node.status({fill:"green",shape:"dot",text:"Not Motion"}) +} \ No newline at end of file diff --git a/front-porch/shut-off.js b/front-porch/shut-off.js new file mode 100644 index 0000000..bf05f4c --- /dev/null +++ b/front-porch/shut-off.js @@ -0,0 +1,20 @@ +const states = global.get('homeassistant.homeAssistant.states') +const frontPorchToggle = states['input_boolean.front_porch_light_on'].state +const deliveryMode = states['input_boolean.delivery_mode'].state +const holidaymode = states['input_boolean.holiday_mode'].state +const currentholiday = states['input_select.holiday_animation'].state +const holiday = currentholiday.toLowerCase() + +if (frontPorchToggle === 'on' && deliveryMode === 'off') { + if (holidaymode === 'on') { + node.status({fill:"blue",shape:"dot",text:"Holiday On"}) + node.send([msg,null]) + } else { + node.status({fill:"green",shape:"dot",text:"Holiday Off"}) + node.send([null,msg]) + } +} else if (frontPorchToggle === 'off') { + node.status({fill:"red",shape:"ring",text:"Blocked (Automation Off"}) +} else if (deliveryMode === 'on') { + node.status({fill:"red",shape:"ring",text:"Blocked (Delivery Mode)"}) +} \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..2722db2 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Node", + "target": "ES2020", + "jsx": "react", + "strictNullChecks": true, + "strictFunctionTypes": true + } +} \ No newline at end of file diff --git a/laundry/processing-start.js b/laundry/processing-start.js new file mode 100644 index 0000000..5f47447 --- /dev/null +++ b/laundry/processing-start.js @@ -0,0 +1,17 @@ +const washerCycle = 'input_number.washer_cycle_length' +const washerFinished = 'input_boolean.washer_finished' +const washerTimer = 'timer.washer_timer' +const washerDateTime = 'input_datetime.washer_finished' +const dryerCycle = 'input_number.dryer_cycle_length' +const dryerFinished = 'input_boolean.dryer_finished' +const dryerTimer = 'timer.dryer_timer' +const dryerDateTime = 'input_datetime.dryer_finished' + +flow.set("washerCycle", washerCycle, "diskCon") +flow.set("washerFinished", washerFinished, "diskCon") +flow.set("washerTimer", washerTimer, "diskCon") +flow.set("washerDateTime", washerDateTime, "diskCon") +flow.set("dryerCycle", dryerCycle, "diskCon") +flow.set("dryerFinished", dryerFinished, "diskCon") +flow.set("dryerTimer", dryerTimer, "diskCon") +flow.set("dryerDateTime", dryerDateTime, "diskCon") \ No newline at end of file diff --git a/laundry/processing.js b/laundry/processing.js new file mode 100644 index 0000000..91ca99e --- /dev/null +++ b/laundry/processing.js @@ -0,0 +1,153 @@ +// Set Constants +const states = global.get('homeassistant.homeAssistant.states') +const textAllowed = states['input_boolean.laundry_notifications_text'].state +const ttsAllowed = states['input_boolean.laundry_notifications_tts'].state +const washerCycle = flow.get("washerCycle", "diskCon") +const washerFinished = flow.get("washerFinished", "diskCon") +const washerTimer = flow.get("washerTimer", "diskCon") +const washerDateTime = flow.get("washerDateTime", "diskCon") +const dryerCycle = flow.get("dryerCycle", "diskCon") +const dryerFinished = flow.get("dryerFinished", "diskCon") +const dryerTimer = flow.get("dryerTimer", "diskCon") +const dryerDateTime = flow.get("dryerDateTime", "diskCon") +const topic = msg.topic +const payload = msg.payload + +// Init variables +let timerEntity = [] +let boolFinished = [] +let servFinished = {} +let dateTimeEntity = [] +let servTimer = {} +let setCycle = [] +let dateTime = [] +let deviceName = {} +let notifyMsg = {} +let ttsMsg = {} + +// Set entity IDs and states +if (topic === 'washer') { + setCycle = washerCycle + boolFinished = washerFinished + dateTimeEntity = washerDateTime + timerEntity = washerTimer + deviceName = 'washer' +} else if (topic === 'dryer') { + setCycle = dryerCycle + boolFinished = dryerFinished + dateTimeEntity = dryerDateTime + timerEntity = dryerTimer + deviceName = 'dryer' +} + +// Get states from the entities +// @ts-ignore +let cycle = states[setCycle].state +// @ts-ignore +let timerState = states[timerEntity].state +// @ts-ignore +let finishedState = states[boolFinished].state + +// Convert minutes into seconds for the timer +let timerDuration = cycle * 60 +let notifyDuration = Math.round(cycle) + +// Decide services +if (payload === 'start') { + servFinished = 'turn_off' + notifyMsg = 'The ' + deviceName + ' has been started for a ' + notifyDuration + ' minute cycle' +} else if (payload === 'finish') { + dateTime = msg.datetime + servFinished = 'turn_on' + notifyMsg = 'The ' + deviceName + ' has finished its cycle' + ttsMsg = 'The ' + deviceName + ' has finished its cycle. I repeat, the ' + deviceName + ' has finished its cycle.' +} else if (payload === 'cancel') { + notifyMsg = 'The ' + deviceName + ' cycle has been cancelled' +} + +// Prepare message payloads +let sendTimerStart = { + "payload": { + "domain": "timer", + "service": "start", + "target": { + "entity_id": timerEntity + }, + "data": { + "duration": timerDuration + } + } +} + +let sendTimerCancel = { + "payload": { + "domain": "timer", + "service": "cancel", + "target": { + "entity_id": timerEntity + }, + "data": {} + } +} + +let sendBoolFinished = { + "payload": { + "domain": "input_boolean", + "service": servFinished, + "target": { + "entity_id": boolFinished + }, + "data": {} + } +} + +let sendDateTimeFinished = { + "payload": { + "domain": "input_datetime", + "service": "set_datetime", + "target": { + "entity_id": dateTimeEntity + }, + "data": { + "datetime": dateTime + } + } +} + +let sendTextNotify = { + "payload": { + "data": { + "type": "normal", + "who": "all", + "title": "Laundry Tracking", + "message": notifyMsg + } + } +} + +let sendTTSNotify = { + "payload": ttsMsg, + "type": "alert", + "topic": "everywhere" +} + +if (payload === 'start') { + node.send([sendBoolFinished,sendTimerStart,null,null]) + node.status({fill:"green",shape:"dot",text:"Starting " + deviceName + " timer for " + notifyDuration + " minutes"}) + node.log("Starting " + deviceName + " timer for " + notifyDuration + " minutes") +} else if (payload === 'finish') { + node.send([[sendBoolFinished,sendDateTimeFinished],null,null]) + if (ttsAllowed === 'on') { + node.send([null,null,null,sendTTSNotify]) + } + node.status({fill:"green",shape:"dot",text:"The " + deviceName + " has finished"}) + node.log("The " + deviceName + " has finished") +} else if (payload === 'cancel') { + node.send([null,sendTimerCancel,null,null]) + node.status({fill:"red",shape:"ring",text:"The " + deviceName + " has been cancelled"}) + node.log("The " + deviceName + " has been cancelled") +} + +if (textAllowed === 'on') { + node.send([null,null,sendTextNotify,null]) +} \ No newline at end of file diff --git a/laundry/timer-change.js b/laundry/timer-change.js new file mode 100644 index 0000000..37675e7 --- /dev/null +++ b/laundry/timer-change.js @@ -0,0 +1,40 @@ +const states = global.get('homeassistant.homeAssistant.states') +const oldState = msg.data.old_state.state +const newState = msg.data.new_state.state +const entity_id = msg.data.entity_id +const washerCycle = flow.get("washerCycle", "diskCon") +const dryerCycle = flow.get("dryerCycle", "diskCon") + +const subtract = newState - oldState +const multiply = subtract * 60 +const diff = parseInt(multiply) + +let machine = {} +let timer = {} + +if (entity_id === washerCycle) { + machine = 'washer' +} else if (entity_id === dryerCycle) { + machine = 'dryer' +} + +if (machine === 'washer') { + timer = flow.get("washerTimer", "diskCon") +} else if (machine === 'dryer') { + timer = flow.get("dryerTimer", "diskCon") +} + +// @ts-ignore +let running = states[timer].state + +let changeMsg = { + "timer": timer, + "change": diff +} + +if (running === 'active') { + node.send(changeMsg) + node.status({fill:"green",shape:"dot",text:"Timer Changed"}) +} else { + node.status({fill:"red",shape:"ring",text:"Blocked"}) +} \ No newline at end of file diff --git a/lightfx/lightning-processing.js b/lightfx/lightning-processing.js new file mode 100644 index 0000000..709bc43 --- /dev/null +++ b/lightfx/lightning-processing.js @@ -0,0 +1,63 @@ +const states = global.get('homeassistant.homeAssistant.states') +const allowed = states["binary_sensor.audible_weather_alerts_allowed"].state +const occupied = states["binary_sensor.basement_occupied"].state +const quiet = states["input_boolean.studio_quiet"].state +const deskState = states["light.tina_desk_lights"].state +const livingRoomState = states["light.living_room_lights"].state +const diningRoomState = states["light.dining_room_lamp"].state +const basementState = states["light.basement_studio_lights"].state +const who = states["binary_sensor.audible_weather_alerts_allowed"].attributes.location +const duration = msg.duration +const payload = msg.payload + +let firstFloorLights = [] +let basementLights = [] + +if (deskState === 'on') { + firstFloorLights.push('light.tina_desk_strip','light.tina_lamp_top','light.tina_lamp_side') +} + +if (livingRoomState === 'on') { + firstFloorLights.push('light.living_room_color_1','light.living_room_color_2','light.living_room_color_3','light.living_room_led_strip') +} + +if (diningRoomState === 'on') { + firstFloorLights.push('light.dining_room_lamp') +} + +if (basementState === 'on') { + basementLights.push('light.basement_tall_lamp','light.basement_short_lamp','light.basement_stairwell','light.basement_led_strip_1') +} + +flow.set("deskState", deskState) +flow.set("livingRoomState", livingRoomState) +flow.set("diningRoomState", diningRoomState) +flow.set("basementState", basementState) + +let firstFloorMsg = { + "payload": payload, + "duration": duration, + "backup": firstFloorLights, + "who": who +} + +let basementMsg = { + "payload": payload, + "duration": duration, + "backup": basementLights +} + +if (allowed === 'on') { + if (occupied === 'on' && quiet === 'off') { + flow.set("basementTrigger","true") + node.status({fill:"green",shape:"dot",text:"Both"}) + return[firstFloorMsg,basementMsg] + } else { + flow.set("basementTrigger","false") + node.status({fill:"green",shape:"dot",text:"Living Room"}) + return[firstFloorMsg,null] + } +} else { + node.status({fill:"red",shape:"ring",text:"Blocked"}) + return null +} \ No newline at end of file diff --git a/lightfx/processing.js b/lightfx/processing.js new file mode 100644 index 0000000..fa6d3ec --- /dev/null +++ b/lightfx/processing.js @@ -0,0 +1 @@ +const states = global.get('homeassistant.homeAssistant.states') diff --git a/sports/events-today.js b/sports/events-today.js new file mode 100644 index 0000000..88caa47 --- /dev/null +++ b/sports/events-today.js @@ -0,0 +1,34 @@ +const states = global.get('homeassistant.homeAssistant.states') +const teams = flow.get("teams", "diskCon") +let teamsToday = [] +let options = [] + +for (let index = 0; index < teams.length; index++) { + const element = teams[index]; + let sensor = "sensor." + element + let binary_today = "binary_sensor." + element + "_event_today" + let binary_inhibit = "binary_sensor." + element + "_inhibit" + let today = states[binary_today].state + let inhibit = states[binary_inhibit].state + let teamName = states[sensor].attributes.friendly_name + if (today === 'on' && inhibit === 'off') { + teamsToday.push(teamName) + } +} + +flow.set("teamsToday", teamsToday, "diskCon") + +if (teamsToday.length > 0) { + options = teamsToday + node.status({fill:"green",shape:"dot",text:"Events today: " + teamsToday.length}) +} else { + options = ['No events today'] + node.status({fill:"red",shape:"ring",text:"No events today"}); +} + +msg.payload = { + "options": options +} + +node.send(msg) +node.done \ No newline at end of file diff --git a/sports/notes.json b/sports/notes.json new file mode 100644 index 0000000..9657688 --- /dev/null +++ b/sports/notes.json @@ -0,0 +1,10 @@ +{ + "teamScore": 0, + "teamPreviousScore": 0, + "_msgid": "75e0427b3d2626fd" +} +{ + "oppScore": 0, + "oppPreviousScore": 0, + "_msgid": "c9945886cc6bd22e" +} \ No newline at end of file diff --git a/sports/scores.js b/sports/scores.js new file mode 100644 index 0000000..2547660 --- /dev/null +++ b/sports/scores.js @@ -0,0 +1,24 @@ +// Get sensor +const states = global.get('homeassistant.homeAssistant.states') +const active = states['switch.watching_sports'].state + +if (active === 'on') { + // Get scores + const team = flow.get("team") + const teamSensor = states["sensor." + team] + const status = teamSensor.state + if (status === 'IN') { + const teamScore = teamSensor.attributes.team_score + const oppScore = teamSensor.attributes.opponent_score + msg = { + "teamscore": teamScore, + "oppscore": oppScore + } + node.status({fill:"green",shape:"dot",text:"Score: " + teamScore + "-" + oppScore}) + node.send(msg) + } else { + node.status({fill:"red",shape:"ring",text:"Not in game"}) + } +} else { + node.status({fill:"red",shape:"ring",text:"Inactive"}) +} \ No newline at end of file diff --git a/time-based/old/early-night.js b/time-based/old/early-night.js new file mode 100644 index 0000000..c2b76c6 --- /dev/null +++ b/time-based/old/early-night.js @@ -0,0 +1,11 @@ +var daynight = msg.payload + +if (daynight === 0) { + node.status({fill:"red",shape:"ring",text:"Off"}); + msg.payload = "off" +} else if (daynight === 1) { + node.status({fill:"green",shape:"dot",text:"On"}); + msg.payload = "on" +} + +return msg; \ No newline at end of file diff --git a/time-based/old/holiday-day.js b/time-based/old/holiday-day.js new file mode 100644 index 0000000..a476c08 --- /dev/null +++ b/time-based/old/holiday-day.js @@ -0,0 +1,20 @@ +var states = global.get('homeassistant.homeAssistant.states') +var holidayMode = states['input_boolean.holiday_mode'].state +var holidayHold = states['input_boolean.holiday_mode_hold'].state +var currentHoliday = states['input_select.holiday_animation'].state +var holiday = currentHoliday.toLowerCase() + +if (holidayMode === 'on') { + if (holidayHold === 'off') { + node.status({fill:"green",shape:"dot",text:"Holiday On" }); + msg.holiday = holiday + return msg; + } else { + node.status({fill:"yellow",shape:"dot",text:"Holiday Hold On"}); + msg.holiday = holiday + return msg; + } +} else { + node.status({fill:"red",shape:"ring",text:"Holiday Off"}); + return null; +} \ No newline at end of file diff --git a/time-based/old/holiday-night.js b/time-based/old/holiday-night.js new file mode 100644 index 0000000..b445b3a --- /dev/null +++ b/time-based/old/holiday-night.js @@ -0,0 +1,13 @@ +var states = global.get('homeassistant.homeAssistant.states') +var holidayMode = states['input_boolean.holiday_mode'].state +var currentHoliday = states['input_select.holiday_animation'].state +var holiday = currentHoliday.toLowerCase() + +if (holidayMode === 'on') { + msg.holiday = holiday + node.status({fill:"green",shape:"dot",text:"Holiday On"}); + return msg; +} else { + node.status({fill:"red",shape:"ring",text:"Holiday Off"}); + return null; +} \ No newline at end of file diff --git a/time-based/old/sunset-lights.js b/time-based/old/sunset-lights.js new file mode 100644 index 0000000..9b13089 --- /dev/null +++ b/time-based/old/sunset-lights.js @@ -0,0 +1,18 @@ +var states = global.get('homeassistant.homeAssistant.states') +var sunsetLights = states['input_boolean.sunset_lights_on'].state +var weather = states['weather.iron_nerd_weather_station'].state + +if (sunsetLights === 'on') { + if (weather === 'sunny' || weather === 'clear-night') { + node.status({fill:"green",shape:"dot",text:"Nice Weather"}); + return[msg,null,null] + } else { + node.status({fill:"grey",shape:"dot",text:"Shitty Weather"}); + return[null,msg,null] + } +} else { + node.status({fill:"red",shape:"ring",text:"Disabled"}); + return[null,null,msg] +} + +return msg; \ No newline at end of file diff --git a/time-based/old/work-processing.js b/time-based/old/work-processing.js new file mode 100644 index 0000000..142163b --- /dev/null +++ b/time-based/old/work-processing.js @@ -0,0 +1,26 @@ +var tomorrow = msg.tomorrow +var today = msg.today +var number = {} +var work_tomorrow = {} + +if (tomorrow > 0) { + work_tomorrow = "true" + if (today == 0) { + number = 0 + } else { + number = 1 + } +} else { + work_tomorrow = "false" + number = 0 +} + +msg.work_tomorrow = work_tomorrow + +if (number == 0) { + node.status({fill:"green",shape:"dot",text:"Number 0"}) + return[msg,null] +} else { + node.status({fill:"green",shape:"dot",text:"Number 1"}) + return[null,msg] +} \ No newline at end of file diff --git a/time-based/processing-start.js b/time-based/processing-start.js new file mode 100644 index 0000000..0f0b933 --- /dev/null +++ b/time-based/processing-start.js @@ -0,0 +1,87 @@ +//! ---------- DAY MODE ---------- +// Lists of adaptive lighting switches for different scenarios +const adaptiveDay = [ + "switch.adaptive_lighting_basement_studio", + "switch.adaptive_lighting_dining_room_lamp", + "switch.adaptive_lighting_downstairs_bathroom", + "switch.adaptive_lighting_emma_bedroom", + "switch.adaptive_lighting_kallen_bedroom", + "switch.adaptive_lighting_living_room", + "switch.adaptive_lighting_master_bedroom", + "switch.adaptive_lighting_mud_room", + "switch.adaptive_lighting_tina_lamp", + "switch.adaptive_lighting_upstairs_bathroom", + "switch.adaptive_lighting_upstairs_hallway", + "switch.adaptive_lighting_front_porch" +] + +const adaptiveDayHoliday = [ + "switch.adaptive_lighting_basement_studio", + "switch.adaptive_lighting_dining_room_lamp", + "switch.adaptive_lighting_downstairs_bathroom", + "switch.adaptive_lighting_emma_bedroom", + "switch.adaptive_lighting_kallen_bedroom", + "switch.adaptive_lighting_living_room", + "switch.adaptive_lighting_master_bedroom", + "switch.adaptive_lighting_mud_room", + "switch.adaptive_lighting_tina_lamp", + "switch.adaptive_lighting_upstairs_bathroom", + "switch.adaptive_lighting_upstairs_hallway", +] + +// List of adaptive lighting sleep mode switches to turn off +const adaptiveSleep = [ + "switch.adaptive_lighting_sleep_mode_basement_studio", + "switch.adaptive_lighting_sleep_mode_dining_room_lamp", + "switch.adaptive_lighting_sleep_mode_downstairs_bathroom", + "switch.adaptive_lighting_sleep_mode_emma_bedroom", + "switch.adaptive_lighting_sleep_mode_kallen_bedroom", + "switch.adaptive_lighting_sleep_mode_living_room", + "switch.adaptive_lighting_sleep_mode_master_bedroom", + "switch.adaptive_lighting_sleep_mode_mud_room", + "switch.adaptive_lighting_sleep_mode_tina_lamp", + "switch.adaptive_lighting_sleep_mode_upstairs_bathroom", + "switch.adaptive_lighting_sleep_mode_upstairs_hallway" +] + +// List of selected scene input texts to reset to Adaptive +const selScenesMain = [ + "input_text.basement_studio_selected_scene", + "input_text.dining_room_lamp_selected_scene", + "input_text.downstairs_bathroom_selected_scene", + "input_text.emma_bedroom_selected_scene", + "input_text.front_porch_selected_scene", + "input_text.kallen_bedroom_selected_scene", + "input_text.living_room_selected_scene", + "input_text.master_bedroom_selected_scene", + "input_text.mud_room_selected_scene", + "input_text.tina_lamp_selected_scene", + "input_text.upstairs_bathroom_selected_scene", + "input_text.upstairs_hallway_selected_scene" +] + +// Tina's desk reset +const selScenesTinaDesk = ["input_text.tina_desk_selected_scene"] + +// List of booleans to turn off +const booleanOff = [ + "input_boolean.give_me_darkness", + "input_boolean.goodnight", + "input_boolean.kallen_computer_updates", + "input_boolean.night_mode" +] + +//! ---------- NIGHT MODE ---------- +// Booleans to turn on at night +const adaptiveNight = [ + "switch.adaptive_lighting_front_porch" +] + + +flow.set("adaptiveDay", adaptiveDay, "diskCon") +flow.set("adaptiveDayHoliday", adaptiveDayHoliday, "diskCon") +flow.set("adaptiveNight", adaptiveNight, "diskCon") +flow.set("adaptiveSleep", adaptiveSleep, "diskCon") +flow.set("selScenesMain", selScenesMain, "diskCon") +flow.set("selScenesTinaDesk", selScenesTinaDesk, "diskCon") +flow.set("booleanOff", booleanOff, "diskCon") \ No newline at end of file diff --git a/time-based/processing.js b/time-based/processing.js new file mode 100644 index 0000000..90d1433 --- /dev/null +++ b/time-based/processing.js @@ -0,0 +1,378 @@ +//! FLOW VARIABLES DEFINED IN START TAB + +// Set Constants + +const states = global.get('homeassistant.homeAssistant.states') +const holidayMode = states['input_boolean.holiday_mode'].state +const holidayHold = states['input_boolean.holiday_mode_hold'].state +const currentHoliday = states['input_select.holiday_animation'].state +const holiday = currentHoliday.toLowerCase() +const sunsetLights = states['input_boolean.sunset_lights_on'].state +const weather = states['weather.iron_nerd_weather_station'].state +const vacation = states['input_boolean.vacation_mode'].state +const upBathOcc = states['binary_sensor.upstairs_bathroom_occupied'].state +const deskLights = states['light.tina_desk_lights'].state +const adaptiveSleep = flow.get("adaptiveSleep", "diskCon") +const selScenesMain = flow.get("selScenesMain", "diskCon") +const selScenesTinaDesk = flow.get("selScenesTinaDesk", "diskCon") +const booleanOff = flow.get("booleanOff", "diskCon") +const payload = msg.payload +node.log("Time-based Automations: Constants Set") + +// Set a few important variables + +let time = {} +let topic = {} +let delay = {} + +if (payload == 0 || payload === 'weather') { + time = "night" +} else if (payload == 1) { + time = "day" +} + +if (msg.topic === 'timer-finished' || msg.topic === 'manual-trigger' || msg.topic === 'weather') { + topic = msg.topic +} + +if (topic === 'timer-finished' || topic === 'manual-trigger') { + delay = 'off' +} else if (topic === 'weather') { + delay = 'off' +} else if (weather === 'sunny' || weather === 'clear-night') { + delay = 'on' +} else { + delay = 'off' +} +node.log("Time-based Automations: Main variables defined") + +// ---------- Configuration ---------- +// Decide which nighttime lighting to turn off at sunrise +let lightsOff = ["light.hallway_overhead"] + +if (holidayHold === 'off') { + lightsOff.push("light.front_porch_light") +} + +if (upBathOcc === 'off') { + lightsOff.push("light.upstairs_bathroom_lights") +} + +// Stairwell light strip settings +let stairKelvin = 2000 +let stairBrt = {} +let stairEntity = ["light.stairwell_led_strip"] +if (time === 'day') { + stairBrt = 255 +} else if (time === 'night') { + stairBrt = 150 +} + +// Sunset lights timer settings +let timerEntity = ["timer.sunset_lighting_timer"] +let timerDuration = "00:30:00" + +// Adaptive lighting switches to set +let switchAdaptive = [] +let setAdaptive = {} + +if (time === 'day') { + if (holidayHold === 'on'){ + switchAdaptive = flow.get("adaptiveDayHoliday", "diskCon") + } else { + switchAdaptive = flow.get("adaptiveDay", "diskCon") + } + setAdaptive = 'on' +} else if (time === 'night') { + switchAdaptive = flow.get("adaptiveNight", "diskCon") + if (holidayMode === 'on') { + setAdaptive = 'off' + } else { + setAdaptive = 'on' + } +} + +// Notification settings +let notifyTitle = {} +let notifyMsg = {} +let sunsetStatus = {} +if (sunsetLights === 'off') { + notifyTitle = "Sunset Lights SKIPPED" + notifyMsg = "Lights not on due to nobody home, or toggle shutoff" + sunsetStatus = "Skipped" +} else { + notifyTitle = "Sunset Lights On" + if (topic === 'weather') { + notifyMsg = "Lights on, delay cancelled due to weather change" + sunsetStatus = "Weather Change" + } else if (delay === 'on') { + notifyMsg = "Lights on after delay due to clear weather" + sunsetStatus = "Delayed" + } else if (delay === 'off') { + notifyMsg = "Lights on early due to cloudy weather" + sunsetStatus = "Early" + } +} + +// Holiday Settings +let setHoliday = {} +let switchHoliday = ["switch.animated_scene_" + holiday] +if (holidayMode === 'on') { + if (time === 'day') { + setHoliday = 'off' + } else if (time === 'night') { + setHoliday = 'on' + } +} + +node.log("Time-based Automations: Decision Logic Complete") + +// ---------- Service Calls ---------- +let sendLights = { + "payload": { + "domain": "light", + "service": "turn_off", + "target": { + "entity_id": lightsOff + }, + "data": {} + } +} + +let sendStairwellScript = { + "payload": { + "domain": "script", + "service": "stairwell_led_strip", + "data": { + "color_temp_kelvin": stairKelvin, + "brightness": stairBrt + } + } +} + +let sendStairwellOff = { + "payload": { + "domain": "light", + "service": "turn_off", + "target": { + "entity_id": stairEntity + }, + "data": {} + } +} + +let sendSleepOff = { + "payload": { + "domain": "switch", + "service": "turn_off", + "target": { + "entity_id": adaptiveSleep + }, + "data": {} + } +} + +let sendBooleanOff = { + "payload": { + "domain": "input_boolean", + "service": "turn_off", + "target": { + "entity_id": booleanOff + }, + "data": {} + } +} + +let sendAdaptive = { + "payload": { + "domain": "switch", + "service": "turn_" + setAdaptive, + "target": { + "entity_id": switchAdaptive + }, + "data": {} + } +} + +let sendSceneResetMain = { + "payload": { + "domain": "input_text", + "service": "set_value", + "target": { + "entity_id": selScenesMain + }, + "data": { + "option": "Adaptive" + } + } +} + +let sendSceneResetDesk = { + "payload": { + "domain": "input_text", + "service": "set_value", + "target": { + "entity_id": selScenesTinaDesk + }, + "data": { + "option": "Day Mode" + } + } +} + +let sendHoliday = { + "payload": { + "domain": "switch", + "service": "turn_" + setHoliday, + "target": { + "entity_id": switchHoliday + }, + "data": {} + } +} + +let sendTimer = { + "payload": { + "domain": "timer", + "service": "start", + "target": { + "entity_id": timerEntity + }, + "data": { + "duration": timerDuration + } + } +} + +let sendTimerCancel = { + "payload": { + "domain": "timer", + "service": "cancel", + "target": { + "entity_id": timerEntity + }, + "data": {} + } +} + +let sendNotifyPhone = { + "payload": { + "domain": "script", + "service": "text_notify", + "data": { + "who": "all", + "title": notifyTitle, + "message": notifyMsg, + "type": "normal", + "tag": "sunset-lights" + } + } +} + +let sendNotifyTV = { + "payload": { + "domain": "script", + "service": "tv_notify", + "data": { + "who": "all", + "title": notifyTitle, + "message": notifyMsg, + "data": { + "fontsize": "large", + "duration": 3, + "transparency": "25%" + } + } + } +} + +let sendFirstFloorScene = { + "payload": { + "domain": "script", + "service": "evening_on_first_floor", + "data": { + "sunset_lights": 1 + } + } +} + +let sendSecondFloorScene = { + "payload": { + "domain": "script", + "service": "evening_on_second_floor", + "data": { + "sunset_lights": 1 + } + } +} + +let sendDeskScene = { + "payload": { + "domain": "input_select", + "service": "select_option", + "target": { + "entity_id": ["input_select.tina_desk_scenes"] + }, + "data": { + "option": "Reset" + } + } +} + +node.log("Time-based Automations: Message Payloads Defined") + +node.log("----- Time-based Automations: Set Parameters") +node.log("time: " + time) +node.log("topic: " + topic) +node.log("lightsOff: " + lightsOff) +node.log("stairKelvin: " + stairKelvin) +node.log("stairBrt: " + stairBrt) +node.log("holidayMode: " + holidayMode) +if (time === 'night') { + node.log("weather: " + weather) + node.log("delay: " + delay) + node.log("sunsetStatus: " + sunsetStatus) +} +if (holidayMode === 'on') { + node.log("switchHoliday: " + switchHoliday) + node.log("holiday: " + holiday) + node.log("holidayHold: " + holidayHold) + node.log("switchHoliday: " + switchHoliday) +} +node.log("----- Time-based Automations: End Parameters -----") + +if (vacation === 'off') { + if (time === "day") { + node.status({fill:"green",shape:"dot",text:"Sunrise Flow"}) + node.send([null,[sendLights,sendBooleanOff],sendStairwellScript]) + setTimeout(() => { + node.send([null,[sendStairwellOff,sendSleepOff,sendAdaptive,sendSceneResetMain,sendSceneResetDesk],null]) + if (holidayMode === 'on' && holidayHold === 'off') { + node.send([null,[sendHoliday],null]) + } + }, 5000) + } else if (time === "night") { + node.status({fill:"green",shape:"dot",text:"Sunset Flow"}) + node.send([null,sendAdaptive,null]) + if (holidayMode === 'on') { + setTimeout(() => { + node.send([null,sendHoliday,null]) + }, 1000) + } + if (delay === 'on') { + node.send([null,sendTimer,null]) + } else { + if (topic === 'weather') { + node.send([null,sendTimerCancel,null]) + } + node.send([null,null,[sendFirstFloorScene,sendSecondFloorScene]]) + if (deskLights === 'on') { + node.send([null,sendDeskScene,null]) + } + if (topic != 'manual-trigger') { + node.send([[sendNotifyPhone,sendNotifyTV],null,null]) + } + } + } +} +node.log("Time-based Automations: Processing Complete") \ No newline at end of file