72 Commits

Author SHA1 Message Date
4029a7992f Kallen bedroom motion lighting fix, for when master bedroom sleeping on 2025-04-30 15:42:25 -04:00
d4bf2e3f23 Facilitate on-the-fly temperature changes in climate watchdog 2025-04-30 15:38:31 -04:00
5d8f69e4f4 Emma bedroom climate watchdog
home_automation/HA-NerdFlows#31
2025-04-24 01:28:39 -04:00
0b64c61cd1 Change name of eco variable in flow context 2025-04-24 01:27:17 -04:00
b6c0ac0713 Add defaults for some variables 2025-04-24 01:27:16 -04:00
bff29b48e9 Handle display screens where needed 2025-04-24 01:27:15 -04:00
bc1ed66b56 Use peopleIDs constant in Emma's climate processing function 2025-04-24 01:27:14 -04:00
fefc52bd01 Standardize some entity constants naming 2025-04-24 01:27:13 -04:00
b626b5b5e8 Master bedroom meltdown center rate-limiting
home_automation/Home-Assistant-Configs#237
2025-04-24 01:27:12 -04:00
408b85bf6a Emma climate flow rate-limiting
home_automation/Home-Assistant-Configs#237
2025-04-24 01:26:59 -04:00
ba56bd08f4 Shower mode nodes rewritten to handle rate-limiting
home_automation/Home-Assistant-Configs#237
2025-04-23 21:54:35 -04:00
b0870578ef Change to switch for eco mode in master bedroom aircon
home_automation/Home-Assistant-Configs#237
2025-04-23 21:18:54 -04:00
da63c34b3d Beginnings of Emma's climate watchdog project
home_automation/HA-NerdFlows#31
2025-04-19 04:03:47 -04:00
ae17ad6969 Remember kids, ONE fucked up dot can ruin everything
home_automation/Home-Assistant-Configs#237
2025-04-19 01:18:32 -04:00
7a957572c8 Change entity ID constants from strings to arrays
home_automation/Home-Assistant-Configs#237
2025-04-18 18:20:10 -04:00
f15aec8e2d Fix log spam when wife's work schedule is not in the calendar 2025-04-16 00:59:27 -04:00
6207e38806 Possible prevention of Kallen's white noise being turned off at 11pm
home_automation/Home-Assistant-Configs#233
2025-04-02 15:54:19 -04:00
8a1f727595 Add date/time to general weather alerts function node status 2025-03-29 23:25:38 -04:00
31b543be65 Rewrite general weather alerts filter 2025-03-29 22:42:27 -04:00
d93d5bd1df Forgot to include topic for all alerts in an area 2025-03-29 22:42:15 -04:00
39c8ed5c9d Rewrite user conversion function 2025-03-29 22:07:06 -04:00
15f1d4970c Updated .gitignore 2025-03-29 21:34:01 -04:00
69099d1d0e Play Emma's door alert on all Echo Dots unless told otherwise 2025-03-29 15:50:12 -04:00
bac4c67530 Add "Alexa Everywhere" to speech engine subflow 2025-03-29 15:45:35 -04:00
114d5884c8 Make Emma's climate flow compatible with white noise 2025-03-29 02:05:39 -04:00
8c1d10f3ef Master bedroom climate flow entity constants 2025-03-29 01:33:27 -04:00
e2c609fc3b Move master bedroom last mode context to object 2025-03-29 01:28:52 -04:00
a585d45606 Move master bedroom aircon installed context to object 2025-03-29 01:22:36 -04:00
c7471fd45b Turn aircon entity ID into a constant 2025-03-29 01:10:04 -04:00
02ce030fe7 Functions code for Emma's bedroom climate flow 2025-03-28 21:35:05 -04:00
71db65fca2 Idiotic typo 2025-03-28 20:21:45 -04:00
3ef08eebf9 Rename last mode context to be room specific 2025-03-28 20:16:25 -04:00
e87275baee Use better templating for node status in master bedroom climate flow 2025-03-26 15:12:01 -04:00
ad6a3db18c Maybe I mean it this time? Heh... 2025-03-26 15:01:06 -04:00
9620f4b4f7 Sleep switch off will cancel Emma's door alert 2025-03-26 14:52:34 -04:00
3574445be0 Fix Emma's door alert not checking if she's sleeping
Also added logging to try and nail down any future issues
2025-03-24 18:34:29 -04:00
1b1048aecd Function code for Emma's door alert 2025-03-24 05:17:02 -04:00
456ec217f2 Rename floor-based folders 2025-03-24 03:09:52 -04:00
6b29b8622f Change temp source to global context for basement heat logic 2025-03-23 14:35:57 -04:00
9d5252c7e4 Improve node status on Tina Work node in presence flow 2025-03-23 13:44:51 -04:00
8a1e0558e8 Small tweak to Work Tomorrow sensor 2025-03-23 04:56:46 -04:00
61c79ca0c0 Improve templating in basement climate flows 2025-03-23 04:42:41 -04:00
09988333f7 Small adjustment in Kallen's timer flow 2025-03-23 04:36:57 -04:00
0fc7fdb31b Improve templating in Kallen's climate flows 2025-03-23 04:32:32 -04:00
10be0213dc Improve templating in master bedroom climate flows 2025-03-23 04:30:10 -04:00
013b482f18 Relocated outdoor temperatures in global context 2025-03-23 01:06:15 -04:00
8dd450940d Cleanup speech engine subflow, and also add it here 2025-03-22 23:44:33 -04:00
bb6dd3cf3e Send notifications to my phone from issue reporter 2025-03-22 23:16:59 -04:00
b6bd149610 User ID conversion subflow 2025-03-22 23:16:16 -04:00
dcb075ed48 Improve status message on local weather alerts node 2025-03-22 23:15:29 -04:00
a1c1e23935 Fix report system to work with Gitea
home_automation/HA-NerdFlows#30
2025-03-22 19:40:34 -04:00
1a4ecc6888 Some cleanup on master bedroom climate processing function 2025-03-22 16:40:49 -04:00
b154903c4d Function to turn off first floor lights when Kallen leaves for school
home_automation/HA-NerdFlows#29
2025-03-22 16:01:26 -04:00
0e13102cde Make folder for goodnight flow because it needs multiple functions 2025-03-22 15:42:40 -04:00
6a6b5c2ae2 Let's try this instead
home_automation/HA-NerdFlows#29
2025-03-22 15:24:24 -04:00
f8b528a9d2 Goodnight should leave first floor alone if Kallen is waiting for school
home_automation/HA-NerdFlows#29
2025-03-22 07:19:37 -04:00
8dd7771eee Remove deprecated service calls
home_automation/HA-NerdFlows#28
2025-03-21 01:28:23 -04:00
c63adaa68d Make area weather alerts reusable as a subflow 2025-03-20 00:33:31 -04:00
3c092bf519 Rename Defiance file for use as local alerts template 2025-03-20 00:04:59 -04:00
19e0e35bf4 Add watches for Defiance 2025-03-19 18:18:35 -04:00
b9a296d312 Improve filter phrases for warnings 2025-03-19 17:56:47 -04:00
503776de4a Add stats for considerable destructive t-storm, confirmed tornado 2025-03-19 17:11:17 -04:00
e37e4d7718 Clean-up severe thunderstorm and tornado alert presentation 2025-03-18 01:13:27 -04:00
2a5aa26e5c Functions for general alerts and Defiance-specific alerts 2025-03-17 01:35:05 -04:00
96c4918d76 Function for filtering weather alerts 2025-03-16 03:59:03 -04:00
b4fbc2f5e6 Continue improving basement heater logic, as the weather warms up 2025-03-11 13:36:49 -04:00
e53e6ab5f2 Improve logic for when basement heater is needed 2025-03-11 02:00:49 -04:00
4a8a9e10de Add function code from "work tomorrow" sensor flow 2025-03-09 14:22:19 -04:00
b628a0b197 Update .gitignore 2025-03-01 00:53:27 -05:00
0f6b6a8275 Fix timer not being canceled when motion is detected again 2025-02-23 22:54:49 -05:00
e23c78c971 Improve motion lighting in some rooms
Motion lighting should no longer reset lights to adaptive when scene or brightness has been changed manually
2025-02-23 01:07:31 -05:00
87ed0e89c0 Use temperature-based preset for basement heater 2025-02-17 06:19:25 -05:00
52 changed files with 1753 additions and 985 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
/.vscode
**.vscode
testing/

View File

@ -1,6 +1,6 @@
const states = global.get('homeassistant.homeAssistant.states')
const allowed = states['input_boolean.basement_studio_heat_allowed'].state
const outsideTemp = states['weather.iron_nerd_weather_station'].attributes.temperature
const outsideTemp = global.get('outdoorTemp.tempInt')
const nightMode = states['input_boolean.night_mode'].state
let power = {}
@ -13,14 +13,14 @@ if (outsideTemp > 32) {
if (nightMode === 'on') {
preset = "frost"
} else {
preset = "eco"
preset = "activity"
}
}
if (allowed === 'on') {
if (power === 'on') {
msg.preset = preset
node.status({fill:'green',shape:'dot',text:'Preset ' + preset})
node.status({fill:'green',shape:'dot',text:`Preset ${preset}`})
node.send(msg,null)
} else {
node.status({fill:'red',shape:'ring',text:'Heater Shutoff'})

View File

@ -0,0 +1,33 @@
const states = global.get('homeassistant.homeAssistant.states')
const allowed = states['input_boolean.basement_studio_heat_allowed'].state
const outsideTemp = global.get('outdoorTemp.tempInt')
const todaysHighTemp = states['sensor.todays_high_temp'].state
const basementTemp = states['sensor.basement_studio_temperature'].state
let power = {}
let reason = {}
if (todaysHighTemp < 55) {
power = 'on'
reason = 'Todays High Temperature'
} else if (outsideTemp < 40) {
power = 'on'
reason = 'Outside Temperature'
} else if (basementTemp < 64) {
power = 'on'
reason = 'Basement Temperature'
} else {
power = 'off'
}
if (allowed === 'on') {
if (power === 'on') {
node.status({fill:'green',shape:'dot',text:`Heater Needed: ${reason}`})
node.send(msg,null)
} else {
node.status({fill:'red',shape:'ring',text:'Heater Not Needed'})
node.send(null,msg)
}
} else {
node.status({fill:'red',shape:'ring',text:'Heat Disabled'})
}

View File

@ -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
const airconEntity = ["climate.emma_bedroom_aircon"]
let setTemp = bedTemp
let setEco = 'turn_off'
let setHvac = 'cool'
let setAcFan = 'High'
let sendHvac = {
"payload": {
"action": "climate.set_hvac_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"hvac_mode": setHvac
}
}
}
let sendTemp = {
"payload": {
"action": "climate.set_temperature",
"target": {
"entity_id": airconEntity
},
"data": {
"temperature": setTemp
}
}
}
let sendEco = {
"payload": {
"action": `switch.${setEco}`,
"target": {
"entity_id": ["switch.emma_bedroom_aircon_eco_mode"]
},
"data": {}
}
}
let sendAcFan = {
"payload": {
"action": "climate.set_fan_mode",
"target": {
"entity_id": airconEntity
},
"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,sendAcFan,sendEco],null])
}
} else {
node.status({fill:"green",shape:"dot",text:"Danger Mode Off"})
node.send([null,reset])
}

View File

@ -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
}

View File

@ -0,0 +1,423 @@
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 schedMode = 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")
// Define reusable constants
const airconEntity = ["climate.emma_bedroom_aircon"]
const whiteNoiseEntity = ["input_boolean.white_noise_emma_bedroom"]
const validSchedModesAC = ['AC', 'Fan']
const peopleIDs = ["input_boolean.emma_awake"]
// 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 = dayTemp
let setAcFan = 'auto'
let setEco = 'turn_on'
let setHvac = 'cool'
let setCool = []
let setSleep = []
let setPeople = []
let setDisplay = 'turn_on'
let setWhiteNoise = "turn_off"
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 = "turn_on"
setHvac = "cool"
} else if (hotDay === 'on' || heatWarning === 'on') {
setTemp = dayTemp
setEco = "turn_on"
setHvac = "cool"
} else {
setTemp = nightTemp
setEco = "turn_on"
setHvac = "off"
}
}
} else if (earlyNight === 'on') {
if (ac === 'on') {
if (danger === 'Extreme') {
setTemp === bedTemp
} else {
setTemp = nightTemp
}
if (schedMode === 'AC') {
setHvac = 'cool'
} else if (schedMode === 'fan') {
setHvac = 'fan_only'
} else {
setHvac = 'off'
}
if (hotDay === 'on') {
setEco = 'turn_off'
} else {
setEco = 'turn_on'
}
}
}
// 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 = 'turn_off'
} else {
setEco = 'turn_on'
}
// Decide HVAC mode
if (schedMode === 'AC') {
setHvac = 'cool'
} else if (schedMode === 'Fan') {
setHvac = 'fan_only'
} else {
setHvac = 'off'
}
// Bed Time
} else if (time === 'bedtime') {
setPeople = 'turn_off'
if (ac === 'on') {
setTemp = bedTemp
setEco = 'turn_off'
if (schedMode === 'AC') {
setHvac = 'cool'
setDisplay = 'turn_off'
} else if (schedMode === 'Fan') {
setHvac = "fan_only"
setDisplay = 'turn_off'
} else if (schedMode === 'White Noise') {
setWhiteNoise = 'turn_on'
if (ac === 'on') {
setHvac = 'off'
}
} 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": peopleIDs
},
"data": {}
}
}
let sendDisplay = {
"payload": {
"action": `switch.${setDisplay}`,
"target": {
"entity_id": ["switch.emma_bedroom_aircon_display"]
},
"data": {}
}
}
let sendWhiteNoise = {
"payload": {
"action": `input_boolean.${setWhiteNoise}`,
"target": {
"entity_id": whiteNoiseEntity
},
"data": {}
}
}
let notify = {
"topic": topic,
"nighttemp": nightTemp,
"schedMode": schedMode
}
let sendHvac = {
"payload": {
"action": "climate.set_hvac_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"hvac_mode": setHvac
}
}
}
let sendTemp = {
"payload": {
"action": "climate.set_temperature",
"target": {
"entity_id": airconEntity
},
"data": {
"temperature": setTemp
}
}
}
let sendEco = {
"payload": {
"action": `switch.${setEco}`,
"target": {
"entity_id": ["switch.emma_bedroom_aircon_eco_mode"]
},
"data": {}
}
}
let sendAcFan = {
"payload": {
"action": "climate.set_fan_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"fan_mode": setAcFan
}
}
}
node.log("Emma Bedroom Climate: Message Payloads Defined")
// Send some parameters to flow context for use in other nodes
if (setHvac.length > 0) {
flow.set('emmaBedroom.airconHvacMode',setHvac,'diskCon')
}
if (setEco.length > 0) {
flow.set('emmaBedroom.airconEco',setEco,'diskCon')
}
if (setTemp.length > 0) {
flow.set('emmaBedroom.airconTargetTemp',setTemp,'diskCon')
}
if (setDisplay.length > 0) {
flow.set('emmaBedroom.airconDisplay',setDisplay,'diskCon')
}
flow.set('emmaBedroom.airconFanMode',setAcFan,'diskCon')
// Log the parameters that were chosen, for debugging purposes
node.log("----- Emma Bedroom Climate: Set Parameters -----")
node.log(`setTemp: ${setTemp}`)
node.log(`setAcFan: ${setAcFan}`)
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([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], 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 (validSchedModesAC.includes(schedMode) && ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null])
node.log("Emma Bedroom Climate: Auto/Bedtime/AC")
} else if (schedMode === 'White Noise') {
node.send([null, sendWhiteNoise, null])
node.log("Emma Bedroom Climate: Auto/Bedtime/White Noise")
}
} 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.send([null, sendWhiteNoise, null])
node.log("Emma Bedroom Climate: Auto/Wakeup/Sleep Off")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], 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([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null])
node.log("Emma Bedroom Climate: Manual/Night/AC")
}
} else if (time === 'day') {
node.status({ fill: "blue", shape: "dot", text: "Manual Day" })
node.send([null, sendWhiteNoise, null])
node.log("Emma Bedroom Climate: Manual/Day")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], 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 (validSchedModesAC.includes(schedMode) && ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null])
node.log("Emma Bedroom Climate: Manual/Bedtime/AC")
} else if (schedMode === 'White Noise') {
node.send([null, sendWhiteNoise, null])
node.log("Emma Bedroom Climate: Manual/Bedtime/White Noise")
}
}
context.set("isWakeup", false)
// Sleep Switch Responses
} else if (type === 'sleep') {
if (payload === 'off') {
node.send([null, [sendPeople, sendWhiteNoise], null])
}
node.log("Emma Bedroom Climate: Sleep")
if (time === 'bedtime') {
node.status({ fill: "blue", shape: "dot", text: "Sleep" })
node.send([null, [sendPeople, sendWhiteNoise], null])
node.log("Emma Bedroom Climate: Sleep/Bedtime")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null])
node.log("Emma Bedroom Climate: Sleep/Bedtime/AC")
}
} else {
node.status({ fill: "blue", shape: "dot", text: "Wakeup" })
node.send([null, sendWhiteNoise, null])
node.log("Emma Bedroom Climate: Sleep/Day")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], 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")

View File

@ -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}`})

View File

@ -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}`})

View File

@ -0,0 +1,21 @@
const topic = msg.topic
const payload = msg.payload
const lastMode = flow.get('emmaBedroom.lastMode', 'diskCon')
if (topic === 'daytemp') {
flow.set('emmaBedroom.dayTemp', payload, 'diskCon')
} else if (topic === 'nighttemp') {
flow.set("emmaBedroom.nightTemp", payload, "diskCon")
} else if (topic === 'bedtemp') {
flow.set("emmaBedroom.bedTemp", payload, "diskCon")
}
if (lastMode === 'day') {
flow.set('emmaBedroom.airconTargetTemp', flow.get('emmaBedroom.dayTemp', 'diskCon'), 'diskCon')
} else if (lastMode === 'night') {
flow.set('emmaBedroom.airconTargetTemp', flow.get('emmaBedroom.nightTemp', 'diskCon'), 'diskCon')
} else if (lastMode === 'bedtime') {
flow.set('emmaBedroom.airconTargetTemp', flow.get('emmaBedroom.bedTemp', 'diskCon'), 'diskCon')
}
node.status({fill:'green',shape:'dot',text:`Set ${topic} to ${payload}`})

View File

@ -0,0 +1,151 @@
const states = global.get('homeassistant.homeAssistant.states')
const sleeping = states['input_boolean.emma_sleeping'].state
if (sleeping !== 'on') {
node.status({fill:'red',shape:'ring',text:'Emma not sleeping, watchdog disabled'})
return null
}
const airconEntity = ['climate.emma_bedroom_aircon']
// Gather relevant attributes from the aircon entity
const airconState = msg.payload.state
const airconAttributes = msg.payload.attributes
const airconEco = airconAttributes['eco_mode']
const airconTargetTemp = airconAttributes['temperature']
const airconFanMode = airconAttributes['fan_mode']
const airconDisplay = airconAttributes['screen_display']
// Gather the last explicitly sent configuration from the context
const airconHvacCfg = flow.get('emmaBedroom.airconHvacMode', 'diskCon')
const airconEcoCfg = flow.get('emmaBedroom.airconEco', 'diskCon')
const airconTargetTempCfg = parseFloat(flow.get('emmaBedroom.airconTargetTemp', 'diskCon'))
const airconFanModeCfg = flow.get('emmaBedroom.airconFanMode', 'diskCon')
const airconDisplayCfg = flow.get('emmaBedroom.airconDisplay', 'diskCon')
// Compare the current state with the last sent configuration
let ecoMatch = {}
let displayMatch = {}
let counter = 0
let sendDisplay = {
"payload": {
"action": `switch.${airconDisplayCfg}`,
"target": {
"entity_id": ["switch.emma_bedroom_aircon_display"]
},
"data": {}
}
}
let sendHvac = {
"payload": {
"action": "climate.set_hvac_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"hvac_mode": airconHvacCfg
}
}
}
let sendTemp = {
"payload": {
"action": "climate.set_temperature",
"target": {
"entity_id": airconEntity
},
"data": {
"temperature": airconTargetTempCfg
}
}
}
let sendEco = {
"payload": {
"action": `switch.${airconEcoCfg}`,
"target": {
"entity_id": ["switch.emma_bedroom_aircon_eco_mode"]
},
"data": {}
}
}
let sendAcFan = {
"payload": {
"action": "climate.set_fan_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"fan_mode": airconFanModeCfg
}
}
}
// Fix hvac mode if mismatch
if (airconState != airconHvacCfg) {
node.log(`Emma Bedroom Aircon HVAC mode mismatch: ${airconState} vs ${airconHvacCfg}`)
node.send(sendHvac)
counter += 1
}
// Fix eco mode if mismatch
if (airconEco === false && airconEcoCfg === 'turn_off') {
ecoMatch = true
} else if (airconEco === true && airconEcoCfg === 'turn_on') {
ecoMatch = true
} else {
ecoMatch = false
}
if (ecoMatch === false) {
node.log(`Emma Bedroom Aircon Eco mode mismatch: ${airconEco} vs ${airconEcoCfg}`)
node.send(sendEco)
counter += 1
}
// Fix target temperature if mismatch
if (airconTargetTemp != airconTargetTempCfg) {
node.log(`Target temperature mismatch: ${airconTargetTemp} vs ${airconTargetTempCfg}`)
node.send(sendTemp)
counter += 1
}
// Fix fan mode if mismatch
if (airconFanMode != airconFanModeCfg) {
node.log(`Fan mode mismatch: ${airconFanMode} vs ${airconFanModeCfg}`)
node.send(sendAcFan)
counter += 1
}
// Fix display mode if mismatch
if (airconDisplay === false && airconDisplayCfg === 'turn_off') {
displayMatch = true
} else if (airconDisplay === true && airconDisplayCfg === 'turn_on') {
displayMatch = true
} else {
displayMatch = false
}
if (displayMatch === false) {
node.log(`Emma Bedroom Aircon Display mode mismatch: ${airconDisplay} vs ${airconDisplayCfg}`)
node.send(sendDisplay)
counter += 1
}
if (counter > 0) {
node.log(`Emma Bedroom Aircon: ${counter} configuration(s) fixed`)
node.status({fill:'yellow',shape:'dot',text:`${counter} config(s) fixed`})
} else {
node.status({fill:'green',shape:'dot',text:`No config changes`})
}

View File

@ -8,10 +8,15 @@ const brightness = states['switch.adaptive_lighting_kallen_bedroom'].attributes.
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 fanState = states['fan.kallen_bedroom_fan'].state
const fanAvailable = fanState !== 'unavailable'
const fanSeparate = states['binary_sensor.kallen_fan_separate_schedule'].state
const schedMode = states['input_select.scheduled_climate_mode_kallen_bedroom'].state
const topic = msg.topic
const toggle = msg.toggle
const toggle = msg.toggle || ''
const roomName = 'Kallen Bedroom'
node.log("Kallen Bedroom: Constants Set")
let setFan = []
@ -19,27 +24,40 @@ let setWhiteNoise = []
let setVolume = []
let setLights = []
let setWake = []
node.log("Kallen Bedroom: Variables Defined")
node.log(`${roomName}: 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 {
if (schedMode === 'White Noise') {
setFan = 'turn_off'
} else {
if (fanAvailable) {
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'
}
} else {
if (schedMode === 'Fan') {
node.log(`${roomName}: Fan is unavailable, forcing White Noise mode`)
}
setFan = 'turn_off'
}
}
if (schedMode === 'White Noise' && toggle === 'on') {
if ((schedMode === 'White Noise' && toggle === 'on') || (schedMode === 'Fan' && !fanAvailable)) {
setWhiteNoise = 'turn_on'
} else if (topic === 'kallen-fan' && !fanAvailable && schedMode !== 'N/A') {
setWhiteNoise = 'turn_on'
} else {
setWhiteNoise = 'turn_off'
}
if (toggle === 'on') {
if (topic === 'boolean' && toggle === 'on') {
setVolume = parseFloat(nightVolume)
setLights = 'turn_on'
setWake = 'turn_off'
} else {
} else if (topic === 'boolean' && toggle === 'off') {
setVolume = parseFloat(dayVolume)
setWake = 'turn_on'
}
@ -49,12 +67,11 @@ let fadeFinal = Math.round(fadeMult)
let brtFinal = Math.round(brightness)
node.log("Kallen Bedroom: Decision Logic Complete")
node.log(`${roomName}: Decision Logic Complete`)
let sendFan = {
"payload": {
"domain": "fan",
"service": setFan,
"action": `fan.${setFan}`,
"target": {
"entity_id": ["fan.kallen_bedroom_fan"]
},
@ -64,8 +81,7 @@ let sendFan = {
let sendWhiteNoise = {
"payload": {
"domain": "input_boolean",
"service": setWhiteNoise,
"action": `input_boolean.${setWhiteNoise}`,
"target": {
"entity_id": ["input_boolean.white_noise_kallen_bedroom"]
},
@ -75,8 +91,7 @@ let sendWhiteNoise = {
let sendVolume = {
"payload": {
"domain": "media_player",
"service": "volume_set",
"action": "media_player.volume_set",
"target": {
"entity_id": ["media_player.kallen_bedroom_google_speaker"]
},
@ -88,8 +103,7 @@ let sendVolume = {
let sendLights = {
"payload": {
"domain": "light",
"service": setLights,
"action": `light.${setLights}`,
"target": {
"entity_id": ["light.kallen_bedroom_lights"]
},
@ -99,8 +113,7 @@ let sendLights = {
let sendWake = {
"payload": {
"domain": "input_boolean",
"service": setWake,
"action": `input_boolean.${setWake}`,
"target": {
"entity_id": ["input_boolean.kallen_awake"]
},
@ -117,33 +130,34 @@ let sleepMsg = {
"payload": "sleep"
}
node.log("Kallen Bedroom: Message Payloads Defined")
node.log(`${roomName}: 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("setWake: " + setWake)
node.log("----- Kallen Bedroom: End Parameters -----")
node.log(`----- ${roomName}: Set Parameters -----`)
node.log(`${roomName}: fanAvailable: ${fanAvailable}`)
node.log(`${roomName}: setFan: ${setFan}`)
node.log(`${roomName}: setWhiteNoise: ${setWhiteNoise}`)
node.log(`${roomName}: setVolume: ${setVolume}`)
node.log(`${roomName}: setLights: ${setLights}`)
node.log(`${roomName}: setWake: ${setWake}`)
node.log(`----- ${roomName}: 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.log(`${roomName}: Early Fan`)
node.send([null,[sendFan,sendWhiteNoise],null])
} else if (toggle === 'off') {
} else if (topic === 'boolean' && toggle === 'off') {
node.status({fill:"green",shape:"dot",text:"Wakeup"})
node.log("Kallen Bedroom: Wake")
node.log(`${roomName}: Wake`)
node.send([wakeMsg,[sendFan,sendWhiteNoise,sendVolume,sendWake],null])
} else if (toggle === 'on') {
} else if (topic === 'boolean' && toggle === 'on') {
node.status({fill:"green",shape:"dot",text:"Sleep"})
node.log("Kallen Bedroom: Sleep")
node.log(`${roomName}: Sleep`)
node.send([null,[sendFan,sendWhiteNoise,sendVolume,sendLights,sendWake],sleepMsg])
}
} else {
node.status({fill:"red",shape:"ring",text:"Blocked"})
node.log("Kallen Bedroom: Flow Blocked")
node.log(`${roomName}: Flow Blocked`)
}
node.log("Kallen Bedroom: Processing Complete")
node.log(`${roomName}: Processing Complete`)

View File

@ -1,21 +1,23 @@
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 ac = global.get('masterBedroom.aircon.installed', "diskCon")
const lastMode = flow.get("masterBedroom.lastMode", "diskCon")
const bedTemp = states['input_number.master_bedroom_bedtime_temp'].state
const airconEntity = ["climate.master_bedroom_aircon"]
const fanEntity = ["fan.master_bedroom_fan"]
let setTemp = bedTemp
let setEco = 'none'
let setEco = 'turn_off'
let setHvac = 'cool'
let setAcFan = 'High'
let setFan = 'turn_off'
let sendFan = {
"payload": {
"domain": "fan",
"service": setFan,
"action": `fan.${setFan}`,
"target": {
"entity_id": ["fan.master_bedroom_fan"]
"entity_id": fanEntity
},
"data": {}
}
@ -23,10 +25,9 @@ let sendFan = {
let sendHvac = {
"payload": {
"domain": "climate",
"service": "set_hvac_mode",
"action": "climate.set_hvac_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": airconEntity
},
"data": {
"hvac_mode": setHvac
@ -36,10 +37,9 @@ let sendHvac = {
let sendTemp = {
"payload": {
"domain": "climate",
"service": "set_temperature",
"action": "climate.set_temperature",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": airconEntity
},
"data": {
"temperature": setTemp
@ -49,23 +49,19 @@ let sendTemp = {
let sendEco = {
"payload": {
"domain": "climate",
"service": "set_preset_mode",
"action": `switch.${setEco}`,
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": ["switch.master_bedroom_aircon_eco_mode"]
},
"data": {
"preset_mode": setEco
}
"data": {}
}
}
let sendAcFan = {
"payload": {
"domain": "climate",
"service": "set_fan_mode",
"action": "climate.set_fan_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": airconEntity
},
"data": {
"fan_mode": setAcFan
@ -81,7 +77,7 @@ 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])
node.send([[sendHvac,sendTemp,sendAcFan,sendEco],null,null])
}
} else {
node.status({fill:"green",shape:"dot",text:"Danger Mode Off"})

View File

@ -4,7 +4,7 @@ const acMode = msg.acmode
const fanMode = msg.fanmode
let coolMsg = {
"payload": 'Master bedroom AC temp has been set to ' + nightTemp + '°F as scheduled',
"payload": `Master bedroom AC temp has been set to ${nightTemp} °F as scheduled`,
"topic": 'AC Mode: Cooling'
}
let fanOnlyMsg = {

View File

@ -3,8 +3,8 @@ node.log("Master Bedroom Climate: Processing Started")
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 ac = global.get('masterBedroom.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.master_bedroom_daytime_temp'].state
@ -25,16 +25,32 @@ 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")
// Define some entity IDs
const airconEntity = ["climate.master_bedroom_aircon"]
const fanEntity = ["fan.master_bedroom_fan"]
const peopleIDs = ["input_boolean.tony_awake", "input_boolean.tina_awake"]
// 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 setTemp = dayTemp
let setAcFan = 'auto'
let setEco = 'auto'
let setHvac = 'cool'
let setFan = []
let setCool = []
let setSleep = []
let setPeople = []
let setDisplay = []
let setDisplay = 'turn_on'
let time = []
let echoDotService = []
let setBriefing = []
@ -85,15 +101,15 @@ if (time === 'day') {
if (ac === 'on') {
if (danger === 'Extreme') {
setTemp = nightTemp
setEco = "eco"
setEco = "turn_on"
setHvac = "cool"
} else if (hotDay === 'on' || heatWarning === 'on') {
setTemp = dayTemp
setEco = "eco"
setEco = "turn_on"
setHvac = "cool"
} else {
setTemp = nightTemp
setEco = "eco"
setEco = "turn_on"
setHvac = "off"
}
}
@ -117,9 +133,9 @@ if (time === 'day') {
setHvac = 'off'
}
if (hotDay === 'on') {
setEco = 'none'
setEco = 'turn_off'
} else {
setEco = 'eco'
setEco = 'turn_on'
}
}
}
@ -139,9 +155,9 @@ if (time === 'day') {
}
// Decide eco mode
if (sleeping === 'on') {
setEco = 'none'
setEco = 'turn_off'
} else {
setEco = 'eco'
setEco = 'turn_on'
}
// Decide HVAC mode
if (acMode === 'AC') {
@ -162,11 +178,13 @@ if (time === 'day') {
setPeople = 'turn_off'
if (ac === 'on') {
setTemp = bedTemp
setEco = 'none'
setEco = 'turn_off'
if (acMode === 'AC') {
setHvac = 'cool'
setDisplay = 'turn_off'
} else if (acMode === 'Fan') {
setHvac = "fan_only"
setDisplay = 'turn_off'
} else {
setHvac = "off"
}
@ -183,10 +201,9 @@ node.log("Master Bedroom Climate: Decision Logic Complete")
let sendFan = {
"payload": {
"domain": "fan",
"service": setFan,
"action": `fan.${setFan}`,
"target": {
"entity_id": ["fan.master_bedroom_fan"]
"entity_id": fanEntity
},
"data": {}
}
@ -194,8 +211,7 @@ let sendFan = {
let sendCool = {
"payload": {
"domain": "input_boolean",
"service": setCool,
"action": `input_boolean.${setCool}`,
"target": {
"entity_id": ["input_boolean.master_bedroom_cooling_on"]
},
@ -205,8 +221,7 @@ let sendCool = {
let sendSleep = {
"payload": {
"domain": "input_boolean",
"service": setSleep,
"action": `input_boolean.${setSleep}`,
"target": {
"entity_id": ["input_boolean.master_bedroom_sleeping"]
},
@ -216,10 +231,9 @@ let sendSleep = {
let sendPeople = {
"payload": {
"domain": "input_boolean",
"service": setPeople,
"action": `input_boolean.${setPeople}`,
"target": {
"entity_id": ["input_boolean.tony_awake","input_boolean.tina_awake"]
"entity_id": peopleIDs
},
"data": {}
}
@ -227,8 +241,7 @@ let sendPeople = {
let sendDisplay = {
"payload": {
"domain": "switch",
"service": setDisplay,
"action": `switch.${setDisplay}`,
"target": {
"entity_id": ["switch.master_bedroom_aircon_display"]
},
@ -245,18 +258,16 @@ let notify = {
let sendBriefing = {
"payload": {
"domain": "script",
"service": setBriefing
"action": `script.${setBriefing}`,
},
"delay": setBriefingDelay
}
let sendHvac = {
"payload": {
"domain": "climate",
"service": "set_hvac_mode",
"action": "climate.set_hvac_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": airconEntity
},
"data": {
"hvac_mode": setHvac
@ -266,10 +277,9 @@ let sendHvac = {
let sendTemp = {
"payload": {
"domain": "climate",
"service": "set_temperature",
"action": "climate.set_temperature",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": airconEntity
},
"data": {
"temperature": setTemp
@ -279,34 +289,29 @@ let sendTemp = {
let sendEco = {
"payload": {
"domain": "climate",
"service": "set_preset_mode",
"action": `switch.${setEco}`,
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": ["switch.master_bedroom_aircon_eco_mode"]
},
"data": {
"preset_mode": setEco
}
"data": {}
}
}
let sendAcFan = {
"payload": {
"domain": "climate",
"service": "set_fan_mode",
"action": "climate.set_fan_mode",
"target": {
"entity_id": ["climate.master_bedroom_aircon"]
"entity_id": airconEntity
},
"data": {
"fan_mode": "auto"
"fan_mode": setAcFan
}
}
}
let sendEchoDotDND = {
"payload": {
"domain": "switch",
"service": echoDotService,
"action": `switch.${echoDotService}`,
"target": {
"entity_id": ["switch.basement_echo_dot_do_not_disturb_switch"]
},
@ -319,19 +324,20 @@ 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("setPeople: " + setPeople)
node.log("setDisplay: " + setDisplay)
node.log("setBriefing: " + setBriefing)
node.log("setBriefingDelay: " + setBriefingDelay)
node.log("time: " + time)
node.log("type: " + type)
node.log("topic: " + topic)
node.log(`setTemp: ${setTemp}`)
node.log(`setAcFan: ${setAcFan}`)
node.log(`setEco: ${setEco}`)
node.log(`setHvac: ${setHvac}`)
node.log(`setFan: ${setFan}`)
node.log(`setCool: ${setCool}`)
node.log(`setSleep: ${setSleep}`)
node.log(`setPeople: ${setPeople}`)
node.log(`setDisplay: ${setDisplay}`)
node.log(`setBriefing: ${setBriefing}`)
node.log(`setBriefingDelay: ${setBriefingDelay}`)
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.
@ -350,14 +356,14 @@ if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'o
} else {
if (topic === 'mrbedroom-cooling' && ac === 'on') {
node.status({ fill: "green", shape: "dot", text: "Cooling Schedule" })
node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null])
node.log("Master Bedroom Climate: Auto/Cooling")
} else if (topic === 'mrbedroom-bedtime') {
node.send([null, null, sendPeople, null, null])
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, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log("Master Bedroom Climate: Auto/Bedtime/AC")
}
if (fanMode === 'fan') {
@ -377,7 +383,7 @@ if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'o
node.send([null, sendFan, null, null, null])
node.log("Master Bedroom Climate: Auto/Wakeup/Sleep Off")
if (ac === 'on') {
node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log("Master Bedroom Climate: Auto/Wakeup/AC On")
}
} else if (sleeping === 'on') {
@ -395,7 +401,7 @@ if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'o
node.send([null, sendFan, null, null, null])
node.log("Master Bedroom Climate: Manual/Night")
if (ac === 'on') {
node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log("Master Bedroom Climate: Manual/Night/AC")
}
} else if (time === 'day') {
@ -403,7 +409,7 @@ if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'o
node.send([null, sendFan, null, null, null])
node.log("Master Bedroom Climate: Manual/Day")
if (ac === 'on') {
node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log("Master Bedroom Climate: Manual/Day/AC")
}
} else if (time === 'bedtime') {
@ -411,7 +417,7 @@ if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'o
node.send([null, sendFan, null, null, null])
node.log("Master Bedroom Climate: Manual/Bedtime")
if (ac === 'on') {
node.send([[sendHvac, sendTemp, sendEco, sendAcFan], null, null, null, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log("Master Bedroom Climate: Manual/Bedtime/AC")
}
}
@ -422,33 +428,25 @@ if (type === 'auto' && allowed === 'on' && meltdown === 'off' && vacation === 'o
node.send([null, null, sendPeople, null, null])
}
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, 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, null])
node.log("Master Bedroom Climate: Sleep/Day")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null, null, null])
node.log("Master Bedroom Climate: Sleep/Day/AC")
}
if (isWakeup === false) {
node.send([null, null, null, null, sendBriefing])
}
} else if (time === 'bedtime') {
if (time === 'bedtime') {
node.status({ fill: "blue", shape: "dot", text: "Sleep" })
node.send([null, sendFan, [sendEchoDotDND,sendPeople], null, null])
node.log("Master Bedroom Climate: Sleep/Bedtime")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendEco, sendAcFan], null, null, null, null])
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log("Master Bedroom Climate: Sleep/Bedtime/AC")
}
} else {
node.status({ fill: "blue", shape: "dot", text: "Wakeup" })
node.send([null, sendFan, sendEchoDotDND, null, null])
node.log("Master Bedroom Climate: Sleep/Day")
if (ac === 'on') {
node.send([[sendDisplay, sendHvac, sendTemp, sendAcFan, sendEco], null, null, null, null])
node.log(`Master Bedroom Climate: Sleep/${convertToTitleCase(time)}/AC`)
}
if (time === 'day' && isWakeup === false) {
node.send([null, null, null, null, sendBriefing])
}
}
context.set("isWakeup", false)
} else if (meltdown === 'on') {

View File

@ -1,4 +1,4 @@
const lastMode = flow.get('lastMode', "diskCon")
const lastMode = flow.get('masterBedroom.lastMode', "diskCon")
const linkSource = msg._linkSource
let setTime = []
@ -27,4 +27,4 @@ let reset = {
}
node.send(reset)
node.status({ fill: "green", shape: "dot", text: "Mode reset to " + setTopic})
node.status({ fill: "green", shape: "dot", text: `Mode reset to ${setTopic}`})

View File

@ -16,5 +16,5 @@ if (sleeping === 'on' || nightMode === 'on' || goodnight === 'on') {
lastMode = 'day'
}
flow.set("lastMode", lastMode, "diskCon")
node.status({ fill: "green", shape: "dot", text: "Last Mode Set: " + lastMode})
flow.set("masterBedroom.lastMode", lastMode, "diskCon")
node.status({ fill: "green", shape: "dot", text: `Last Mode Set: ${lastMode}`})

View File

@ -1,6 +1,6 @@
const states = global.get('homeassistant.homeAssistant.states')
const ac = states['input_boolean.master_bedroom_aircon_installed'].state
const temp = global.get('tempStr')
const ac = global.get('masterBedroom.aircon.installed', "diskCon")
const temp = global.get('outdoorTemp.tempStr')
const threshold = states['input_number.master_bedroom_aircon_run_threshold'].state
const dayTemp = states['input_number.master_bedroom_daytime_temp'].state
const nightTemp = states['input_number.master_bedroom_night_temp'].state
@ -11,31 +11,33 @@ const sleeping = states['input_boolean.master_bedroom_sleeping'].state
const hotDay = states['input_boolean.hot_day'].state
const topic = msg.topic
const airconEntity = ["climate.master_bedroom_aircon"]
let setTemp = {}
let ecoMode = {}
let setEco = {}
let duration = {}
let end = {}
let hvac = {}
let setHvac = {}
if (topic === 'toggle') {
if (hotDay === 'on' || sleeping === 'on') {
setTemp = bedTemp
ecoMode = 'none'
setEco = 'turn_off'
} else {
setTemp = nightTemp
ecoMode = 'eco'
setEco = 'turn_on'
}
} else if (topic === 'timer') {
if (currentSetTemp == bedTemp) {
setTemp = nightTemp
if (hotDay === 'on') {
ecoMode = 'none'
setEco = 'turn_off'
} else {
ecoMode = 'eco'
setEco = 'turn_on'
}
} else {
setTemp = dayTemp
ecoMode = 'eco'
setEco = 'turn_on'
}
}
@ -50,36 +52,75 @@ if (setTemp == bedTemp) {
if (setTemp == dayTemp) {
end = true
if (hotDay === 'off') {
hvac = 'off'
setHvac = 'off'
} else {
hvac = 'cool'
setHvac = 'cool'
}
} else if (setTemp != bedTemp && coolingTime === 'on') {
end = true
hvac = 'cool'
setHvac = 'cool'
} else {
end = false
hvac = 'cool'
}
let setMsg = {
"set": setTemp,
"eco": ecoMode,
"hvac": hvac,
"end": end
setHvac = 'cool'
}
let timerStart = {
"duration": duration
}
let sendHvac = {
"payload": {
"action": "climate.set_hvac_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"hvac_mode": setHvac
}
}
}
let sendTemp = {
"payload": {
"action": "climate.set_temperature",
"target": {
"entity_id": airconEntity
},
"data": {
"temperature": setTemp
}
}
}
let sendEco = {
"payload": {
"action": `switch.${setEco}`,
"target": {
"entity_id": ["switch.master_bedroom_aircon_eco_mode"]
},
"data": {}
}
}
let sendAcFan = {
"payload": {
"action": "climate.set_fan_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"fan_mode": "auto"
}
}
}
if (ac === 'on' && temp >= threshold) {
if (sleeping === 'on' || coolingTime === 'on' || end === true) {
node.status({fill:"green",shape:"dot",text:"Finished"})
return [setMsg, null]
return [[sendHvac, sendTemp, sendAcFan, sendEco], null]
} else {
node.status({fill:"blue",shape:"dot",text:"Next Cycle"})
return [setMsg, timerStart]
return [[sendHvac, sendTemp, sendAcFan, sendEco], timerStart]
}
} else if (ac === 'off') {
node.status({fill:"red",shape:"ring",text:"A/C Not Installed"})

View File

@ -1,22 +1,61 @@
const states = global.get('homeassistant.homeAssistant.states')
const ac = states['input_boolean.master_bedroom_aircon_installed'].state
const temp = global.get('tempStr')
const ac = global.get('masterBedroom.aircon.installed', "diskCon")
const temp = global.get('outdoorTemp.tempStr')
const threshold = states['input_number.master_bedroom_aircon_run_threshold'].state
const nightTemp = states['input_number.master_bedroom_night_temp'].state
const bedTemp = states['input_number.master_bedroom_bedtime_temp'].state
const sleeping = states['input_boolean.master_bedroom_sleeping'].state
const airconEntity = ["climate.master_bedroom_aircon"]
let setTemp = []
let setHvac = "dry"
let setEco = "turn_off"
if (sleeping === 'on') {
setTemp = bedTemp
} else {
setTemp = nightTemp
}
let sendHvac = {
"payload": {
"action": "climate.set_hvac_mode",
"target": {
"entity_id": airconEntity
},
"data": {
"hvac_mode": setHvac
}
}
}
let sendTemp = {
"payload": {
"action": "climate.set_temperature",
"target": {
"entity_id": airconEntity
},
"data": {
"temperature": setTemp
}
}
}
let sendEco = {
"payload": {
"action": `switch.${setEco}`,
"target": {
"entity_id": ["switch.master_bedroom_aircon_eco_mode"]
},
"data": {}
}
}
if (ac === 'on') {
if (temp >= threshold) {
if (sleeping === 'on') {
msg.set = bedTemp
node.status({fill:"green",shape:"dot",text:"Shower Mode On: " + bedTemp})
return msg
} else {
msg.set = nightTemp
node.status({fill:"green",shape:"dot",text:"Shower Mode On: " + nightTemp})
return msg
}
node.status({fill:"green",shape:"dot",text:`Shower Mode On: ${setTemp}°F`})
node.send([[sendHvac, sendTemp, sendEco]])
} else {
node.status({fill:"red",shape:"ring",text:"Too Cold"})
return null

View File

@ -7,17 +7,22 @@ const payload = msg.payload
const newDuration = duration * 60
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -1,5 +1,6 @@
const states = global.get('homeassistant.homeAssistant.states')
const lights = states['light.mud_room_overhead'].state
const selScene = states['input_text.mud_room_selected_scene'].state
const duration = states['input_number.mud_room_lights_off_delay'].state
const lux = parseInt(states['sensor.mud_room_illuminance'].state)
const threshold = parseInt(states['input_number.mud_room_lux_threshold'].state)
@ -7,17 +8,22 @@ const payload = msg.payload
const newDuration = duration * 60
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && (lights === 'off' || selScene === 'Nightlight')) {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -0,0 +1,30 @@
const states = global.get('homeassistant.homeAssistant.states')
const schoolToday = states['input_boolean.kallen_school_today'].state
const kallen = states['person.kallen_stork'].state
const morning = states['binary_sensor.morning'].state
let basement = {
"payload": {
"action": "script.goodnight_in_basement"
}
}
let firstFloor = {
"payload": {
"action": "script.goodnight_on_first_floor"
}
}
let secondFloor = {
"payload": {
"action": "script.goodnight_on_second_floor"
}
}
if (schoolToday === 'on' && kallen === 'home' && morning === 'on') {
node.send([[basement,secondFloor]])
node.status({fill:'yellow',shape:'dot',text:'Kallen waiting for school bus'})
} else {
node.send([[basement,firstFloor,secondFloor]])
node.status({fill:'green',shape:'dot',text:'All scripts sent'})
}

View File

@ -0,0 +1,25 @@
const states = global.get('homeassistant.homeAssistant.states')
const schoolToday = states['input_boolean.kallen_school_today'].state
const kallen = states['person.kallen_stork'].state
const morning = states['binary_sensor.morning'].state
const masterBedroomSleeping = states['input_boolean.master_bedroom_sleeping'].state
const payload = msg.payload
let presence = {}
if (payload === 'home') {
presence = 'on'
} else if (payload != 'unavailable' && payload != 'unknown') {
presence = 'off'
}
if (schoolToday === 'on' && kallen != 'home' && morning === 'on') {
if (presence === 'off' && masterBedroomSleeping === 'on') {
node.send(msg)
node.status({fill:'green',shape:'dot',text:'Kallen left, first floor lights off'})
} else {
return null
}
} else {
return null
}

View File

@ -0,0 +1,20 @@
const statusCode = msg.statusCode
const issueNumber = msg.payload.number
const issueTitle = msg.payload.title
const url = msg.payload.html_url
const user = msg.user
let notification = {
"title": "Issue Submitted",
"message": `${user.firstName} ${user.lastName} submitted a new issue: #${issueNumber} - ${issueTitle}`,
"actions": [
{
"action": "URI",
"uri": url,
"title": "View Issue"
}
]
}
node.send(notification)
node.status({fill:'green',shape:'dot',text:`Issue #${issueNumber} notification sent`})

View File

@ -0,0 +1,33 @@
const needsReponseLabel = flow.get("needsReponseLabel", "diskCon")
const typeBugLabel = flow.get("typeBugLabel", "diskCon")
const typeEnhancementLabel = flow.get("typeEnhancementLabel", "diskCon")
const typeAdjustmentLabel = flow.get("typeAdjustmentLabel", "diskCon")
const title = msg.text
const type = msg.type
const user = msg.user
let labels = [needsReponseLabel]
let formatType = {}
if (type === 'Bug Report') {
labels.push(typeBugLabel)
formatType = "[Bug]"
} else if (type === 'New Idea') {
labels.push(typeEnhancementLabel)
formatType = "[Feature Request]"
} else if (type === 'Adjustment') {
labels.push(typeAdjustmentLabel)
formatType = "[Adjustment Request]"
}
let newTitle = `${formatType}: ${title}`
msg.payload = {
"title": newTitle,
"body": `This issue was submitted by ${user.firstName} ${user.lastName}, using the automated form on the main Home Assistant dashboard`,
"assignees":["tm24fan8"],
"labels": labels
}
node.status({fill:'green',shape:'dot',text:'API data prepped'})
return msg

View File

@ -0,0 +1,21 @@
const states = global.get('homeassistant.homeAssistant.states')
const text = states['input_text.report_system'].state
const type = states['input_select.report_type'].state
const resetText = 'Type your report here'
const resetType = 'Bug Report'
msg.text = text
msg.type = type
msg.reset_text = resetText
msg.reset_type = resetType
if (type === "New Idea") {
node.status({fill:"green",shape:"dot",text:"New Idea"})
} else if (type === "Bug Report") {
node.status({fill:"green",shape:"dot",text:"Bug Report"})
} else if (type === "Adjustment") {
node.status({fill:"green",shape:"dot",text:"Adjustment"})
}
return msg

View File

@ -0,0 +1,9 @@
const needsReponseLabel = 213
const typeBugLabel = 196
const typeEnhancementLabel = 198
const typeAdjustmentLabel = 208
flow.set("needsReponseLabel", needsReponseLabel, "diskCon")
flow.set("typeBugLabel", typeBugLabel, "diskCon")
flow.set("typeEnhancementLabel", typeEnhancementLabel, "diskCon")
flow.set("typeAdjustmentLabel", typeAdjustmentLabel, "diskCon")

View File

@ -6,7 +6,7 @@ if (msg.lights === 'on') {
node.status({fill:"green",shape:"dot",text:"Lights Controlled"})
return[null,msg]
} else {
node.status({fill:"red",shape:"ring",text:"N/A in " + msg.room})
node.status({fill:"red",shape:"ring",text:`N/A in ${msg.room}`})
return null
}
} else {

View File

@ -68,8 +68,7 @@ if (payload === 'start') {
// Prepare message payloads
let sendTimerStart = {
"payload": {
"domain": "timer",
"service": "start",
"action": "timer.start",
"target": {
"entity_id": timerEntity
},
@ -81,8 +80,7 @@ let sendTimerStart = {
let sendTimerCancel = {
"payload": {
"domain": "timer",
"service": "cancel",
"action": "timer.cancel",
"target": {
"entity_id": timerEntity
},
@ -92,8 +90,7 @@ let sendTimerCancel = {
let sendBoolFinished = {
"payload": {
"domain": "input_boolean",
"service": servFinished,
"action": "input_boolean." + servFinished,
"target": {
"entity_id": boolFinished
},
@ -103,8 +100,7 @@ let sendBoolFinished = {
let sendDateTimeFinished = {
"payload": {
"domain": "input_datetime",
"service": "set_datetime",
"action": "input_datetime.set_datetime",
"target": {
"entity_id": dateTimeEntity
},

View File

@ -10,13 +10,6 @@ const segLivingRoom2 = [10,11,12,13,14,15,16,17,18,19]
const segLivingRoom3 = [20,21,22,23,24,25,26,27,28,29]
const segLivingRoom4 = [30,31,32,33,34,35,36,37,38,39]
// Preset effect segments for living room LED strip
const effectSegLivingRoom1 = [].concat(segLivingRoom1, segLivingRoom3)
const effectSegLivingRoom2 = [].concat(segLivingRoom2, segLivingRoom4)
// Full strip for living room LED strip
const livingRoomFull = [].concat(halfLivingRoom1, halfLivingRoom2)
// Half and half for basement LED strip
const halfBasement1 = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
const halfBasement2 = [16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]
@ -27,65 +20,18 @@ const segBasement2 = [8,9,10,11,12,13,14,15]
const segBasement3 = [16,17,18,19,20,21,22,23]
const segBasement4 = [24,25,26,27,28,29,30,31]
// Preset effect segments for basement LED strip
const effectSegBasement1 = [].concat(segBasement1, segBasement3)
const effectSegBasement2 = [].concat(segBasement2, segBasement4)
// Full strip for basement LED strip
const basementFull = [].concat(halfBasement1, halfBasement2)
// Adaptive Lighting switches
const adaptiveFirstFloor = [
"switch.adaptive_lighting_living_room",
"switch.adaptive_lighting_dining_room_lamp",
"switch.adaptive_lighting_tina_lamp"]
const adaptiveBasement = [
"switch.adaptive_lighting_basement_studio"
]
// Define light arrays for backup scenes
const lightsTinaDesk = ['light.tina_desk_strip','light.tina_lamp_top','light.tina_lamp_side']
const lightsBasement = ['light.basement_tall_lamp','light.basement_short_lamp','light.basement_stairwell','light.basement_led_strip_1']
const lightsLivingRoom = ['light.living_room_color_1','light.living_room_color_2','light.living_room_color_3','light.living_room_led_strip']
const lightsDiningRoom = ['light.dining_room_lamp']
const lightsStairwell = ['light.stairwell_led_strip']
// Write flow variables
flow.set("halfLivingRoom1", halfLivingRoom1, "diskCon")
flow.set("halfLivingRoom2", halfLivingRoom2, "diskCon")
flow.set("halfLivingRoom2", halfLivingRoom1, "diskCon")
flow.set("segLivingRoom1", segLivingRoom1, "diskCon")
flow.set("segLivingRoom2", segLivingRoom2, "diskCon")
flow.set("segLivingRoom3", segLivingRoom3, "diskCon")
flow.set("segLivingRoom4", segLivingRoom4, "diskCon")
flow.set("effectSegLivingRoom1", effectSegLivingRoom1, "diskCon")
flow.set("effectSegLivingRoom2", effectSegLivingRoom2, "diskCon")
flow.set("livingRoomFull", livingRoomFull, "diskCon")
flow.set("halfBasement1", halfBasement1, "diskCon")
flow.set("halfBasement2", halfBasement2, "diskCon")
flow.set("halfBasement2", halfBasement1, "diskCon")
flow.set("segBasement1", segBasement1, "diskCon")
flow.set("segBasement2", segBasement2, "diskCon")
flow.set("segBasement3", segBasement3, "diskCon")
flow.set("segBasement4", segBasement4, "diskCon")
flow.set("effectSegBasement1", effectSegBasement1, "diskCon")
flow.set("effectSegBasement2", effectSegBasement2, "diskCon")
flow.set("basementFull", basementFull, "diskCon")
flow.set("adaptiveFirstFloor", adaptiveFirstFloor, "diskCon")
flow.set("adaptiveBasement", adaptiveBasement, "diskCon")
flow.set('lightsTinaDesk', lightsTinaDesk, 'diskCon')
flow.set('lightsBasement', lightsBasement, 'diskCon')
flow.set('lightsLivingRoom', lightsLivingRoom, 'diskCon')
flow.set('lightsDiningRoom', lightsDiningRoom, 'diskCon')
flow.set('lightsStairwell', lightsStairwell, 'diskCon')
flow.set('alertFirstFloor', false)
flow.set('alertBasement', false)

View File

@ -1,368 +1,36 @@
// Set constants
const states = global.get('homeassistant.homeAssistant.states')
const occupied = states["binary_sensor.basement_occupied"].state
const quiet = states["input_boolean.studio_quiet"].state
const stateTinaDesk = states["light.tina_desk_lights"].state
const stateLivingRoom = states["light.living_room_lights"].state
const stateDiningRoom = states["light.dining_room_lamp"].state
const stateStairwell = states["light.stairwell_led_strip"].state
const stateBasement = states["light.basement_studio_lights"].state
const adaptLivingRoom = states["switch.adaptive_lighting_living_room"].state
const adaptDiningRoom = states["switch.adaptive_lighting_dining_room_lamp"].state
const adaptBasement = states["switch.adaptive_lighting_basement_studio"].state
const nightLivingRoom = states["switch.adaptive_lighting_sleep_mode_living_room"].state
const nightDiningRoom = states["switch.adaptive_lighting_sleep_mode_dining_room_lamp"].state
const nightBasement = states["switch.adaptive_lighting_sleep_mode_basement_studio"].state
const currentSceneBasement = states["input_text.basement_studio_selected_scene"].state
const currentSceneLivingRoom = states["input_text.living_room_selected_scene"].state
const currentSceneTinaDesk = states["input_text.tina_desk_selected_scene"].state
const currentSceneDiningRoom = states["input_text.dining_room_lamp_selected_scene"].state
const effectBasement = msg.payload.event.basement
const effectFirstFloor = msg.payload.event.first_floor
const effectColor = msg.payload.event.color
const effectDivide = msg.payload.event.divide
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 where = msg.payload.event.where
const type = msg.payload.event.type
const duration = msg.payload.event.duration
const delay = duration * 1000
const adaptiveSwitchFirstFloor = flow.get("adaptiveFirstFloor", "diskCon")
const adaptiveSwitchBasement = flow.get("adaptiveBasement", "diskCon")
const lightsTinaDesk = flow.get('lightsTinaDesk', 'diskCon')
const lightsBasement = flow.get('lightsBasement', 'diskCon')
const lightsLivingRoom = flow.get('lightsLivingRoom', 'diskCon')
const lightsDiningRoom = flow.get('lightsDiningRoom', 'diskCon')
const alertFirstFloor = flow.get('alertFirstFloor')
const alertBasement = flow.get('alertBasement')
// Set flow contexts to be used later in the reset node
flow.set("currentSceneBasement", currentSceneBasement)
flow.set("currentSceneLivingRoom", currentSceneLivingRoom)
flow.set("currentSceneTinaDesk", currentSceneTinaDesk)
flow.set('currentSceneDiningRoom', currentSceneDiningRoom)
flow.set("adaptLivingRoom", adaptLivingRoom)
flow.set("adaptBasement", adaptBasement)
flow.set("nightLivingRoom", nightLivingRoom)
flow.set("nightBasement", nightBasement)
flow.set('nightDiningRoom', nightDiningRoom)
flow.set("stateTinaDesk", stateTinaDesk)
flow.set("stateLivingRoom", stateLivingRoom)
flow.set("stateDiningRoom", stateDiningRoom)
flow.set("stateStairwell", stateStairwell)
flow.set("stateBasement", stateBasement)
let firstFloorLights = []
let basementLights = []
// Define empty arrays for basement LED strip zones
let basement1 = []
let basement2 = []
let basementFull = []
// Define empty arrays for living room LED strip zones
let livingRoom1 = []
let livingRoom2 = []
let livingRoomFull = []
// Color names are set by default, however certain effects can override them below
let colorName1 = effectColor
let colorName2 = "white"
// Define empty object for LIFX effect settings
let lifxData = {}
let effectLifx = msg.payload.event.lifx
// Setup the proper zones to use for the effect
if (effectDivide === 'half') {
basement1 = flow.get("halfBasement1", "diskCon")
basement2 = flow.get("halfBasement2", "diskCon")
livingRoom1 = flow.get("halfLivingRoom1", "diskCon")
livingRoom2 = flow.get("halfLivingRoom2", "diskCon")
} else if (effectDivide === 'quarter') {
basement1 = flow.get("effectSegBasement1", "diskCon")
basement2 = flow.get("effectSegBasement2", "diskCon")
livingRoom1 = flow.get("effectSegLivingRoom1", "diskCon")
livingRoom2 = flow.get("effectSegLivingRoom2", "diskCon")
} else if (effectDivide === 'full') {
basementFull = flow.get("basementFull", "diskCon")
livingRoomFull = flow.get("livingRoomFull", "diskCon")
if (deskState === 'on') {
firstFloorLights.push('light.tina_desk_strip','light.tina_lamp_top','light.tina_lamp_side')
}
if (effectFirstFloor === true) {
flow.set('alertFirstFloor', true)
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 (effectBasement === true) {
flow.set('alertBasement', true)
if (diningRoomState === 'on') {
firstFloorLights.push('light.dining_room_lamp')
}
// Set/change colors if necessary
if (effectColor === 'police') {
colorName1 = 'red'
colorName2 = 'blue'
if (basementState === 'on') {
basementLights.push('light.basement_tall_lamp','light.basement_short_lamp','light.basement_stairwell','light.basement_led_strip_1')
}
// Define parameters for different LIFX effects
if (effectLifx === 'move') {
lifxData = {
"speed": 0.5,
"direction": "right",
"power_on": false
}
} else if (effectLifx === 'ping') {
effectLifx = 'pulse'
colorName2 = colorName1
lifxData = {
"power_on": false,
"mode": "ping",
"period": 1,
"cycles": duration,
"color_name": "white",
"brightness": 255
}
} else if (effectLifx === 'blink') {
effectLifx = 'pulse'
colorName2 = colorName1
lifxData = {
"power_on": false,
"mode": "blink",
"period": 0.5,
"cycles": duration * 2,
"color_name": "white",
"brightness": 255
}
}
flow.set("deskState", deskState)
flow.set("livingRoomState", livingRoomState)
flow.set("diningRoomState", diningRoomState)
flow.set("basementState", basementState)
// Define messages
let sendBasementStripFX = {
"payload": {
"domain": "lifx",
"service": "effect_" + effectLifx,
"target": {
"entity_id": ["light.basement_led_strip_1"]
},
"data": lifxData
}
}
let sendBasementStrip1 = {
"payload": {
"domain": "lifx",
"service": "set_state",
"target": {
"entity_id": ["light.basement_led_strip_1"]
},
"data": {
"zones": basement1,
"color_name": colorName1,
"brightness": 255
}
}
}
let sendBasementStrip2 = {
"payload": {
"domain": "lifx",
"service": "set_state",
"target": {
"entity_id": ["light.basement_led_strip_1"]
},
"data": {
"zones": basement2,
"color_name": colorName2,
"brightness": 255,
"power": true
}
}
}
let sendBasementLights = {
"payload": {
"domain": "light",
"service": "turn_on",
"target": {
"entity_id": [
"light.basement_tall_lamp",
"light.basement_short_lamp",
"light.basement_stairwell"
]
},
"data": {
"color_name": colorName1,
"brightness": 255
}
}
}
let sendLivingRoomStripFX = {
"payload": {
"domain": "lifx",
"service": "effect_" + effectLifx,
"target": {
"entity_id": ["light.living_room_led_strip"]
},
"data": lifxData
}
}
let sendLivingRoomStrip1 = {
"payload": {
"domain": "lifx",
"service": "set_state",
"target": {
"entity_id": ["light.living_room_led_strip"]
},
"data": {
"zones": livingRoom1,
"color_name": colorName1,
"brightness": 255
}
}
}
let sendLivingRoomStrip2 = {
"payload": {
"domain": "lifx",
"service": "set_state",
"target": {
"entity_id": ["light.living_room_led_strip"]
},
"data": {
"zones": livingRoom2,
"color_name": colorName2,
"brightness": 255,
"power": true
}
}
}
let sendLivingRoomLights = {
"payload": {
"domain": "light",
"service": "turn_on",
"target": {
"entity_id": [
"light.living_room_color_1",
"light.living_room_color_2",
"light.living_room_color_3",
"light.stairwell_led_strip"
]
},
"data": {
"color_name": colorName1,
"brightness": 255
}
}
}
let sendTinaDeskLights = {
"payload": {
"domain": "light",
"service": "turn_on",
"target": {
"entity_id": ["light.tina_desk_lights"]
},
"data": {
"color_name": colorName1,
"brightness": 255
}
}
}
let sendDiningRoomLamp = {
"payload": {
"domain": "light",
"service": "turn_off",
"target": {
"entity_id": ["light.dining_room_lamp"]
},
"data": {}
}
}
let sendAdaptiveFirstFloor = {
"payload": {
"domain": "switch",
"service": "turn_off",
"target": {
"entity_id": adaptiveSwitchFirstFloor
},
"data": {}
}
}
let sendAdaptiveBasement = {
"payload": {
"domain": "switch",
"service": "turn_off",
"target": {
"entity_id": adaptiveSwitchBasement
},
"data": {}
}
}
let backupLivingRoom = {
"payload": {
"domain": "scene",
"service": "create",
"data": {
"scene_id": "living_room_alert_restore",
"snapshot_entities": lightsLivingRoom
}
}
}
let backupTinaDesk = {
"payload": {
"domain": "scene",
"service": "create",
"data": {
"scene_id": "tina_desk_alert_restore",
"snapshot_entities": lightsTinaDesk
}
}
}
let backupBasement = {
"payload": {
"domain": "scene",
"service": "create",
"data": {
"scene_id": "basement_alert_restore",
"snapshot_entities": lightsBasement
}
}
}
let sendReset = {
"delay": delay
}
let sendDebug = {
"topic": "debug",
"colorName1": colorName1,
"colorName2": colorName2,
"lifxData": lifxData,
"effectLifx": effectLifx,
"basement1": basement1,
"basement2": basement2
}
// Send backup messages
if (stateLivingRoom === 'on' && alertFirstFloor === false) {
node.send([backupLivingRoom,null,null,null,null,null])
}
if (stateTinaDesk === 'on' && alertFirstFloor === false) {
node.send([backupTinaDesk,null,null,null,null,null])
}
if (stateBasement === 'on' && alertBasement === false) {
node.send([backupBasement,null,null,null,null,null])
}
// Send effect messages
if (effectBasement === true) {
node.send([null,null,null,null,[sendAdaptiveBasement,sendBasementLights,sendBasementStripFX,sendBasementStrip1,sendBasementStrip2,sendDebug],null])
}
if (effectFirstFloor === true) {
node.send([null,[sendAdaptiveFirstFloor,sendLivingRoomLights,sendLivingRoomStripFX,sendLivingRoomStrip1,sendLivingRoomStrip2],sendTinaDeskLights,sendDiningRoomLamp,null,null])
}
// Send the reset message to start the timer
node.send([null,null,null,null,null,sendReset])
node.status({fill:'green',shape:'dot',text:'Alert Sent'})
// let segLivingRoom = [].concat(segLivingRoom1, segLivingRoom2)

View File

@ -1,326 +0,0 @@
// Set constants
const states = global.get('homeassistant.homeAssistant.states')
const currentSceneBasement = flow.get('currentSceneBasement')
const currentSceneLivingRoom = flow.get('currentSceneLivingRoom')
const currentSceneTinaDesk = flow.get('currentSceneTinaDesk')
const currentSceneDiningRoom = flow.get('currentSceneDiningRoom')
const adaptLivingRoom = flow.get('adaptLivingRoom')
const adaptBasement = flow.get('adaptBasement')
const nightLivingRoom = flow.get('nightLivingRoom')
const nightBasement = flow.get('nightBasement')
const nightDiningRoom = flow.get('nightDiningRoom')
const stateTinaDesk = flow.get('stateTinaDesk')
const stateLivingRoom = flow.get('stateLivingRoom')
const stateDiningRoom = flow.get('stateDiningRoom')
const stateBasement = flow.get('stateBasement')
const stateStairwell = flow.get('stateStairwell')
const scenesLivingRoom = states["input_select.living_room_scenes"].attributes.options
const scenesBasement = states["input_select.basement_studio_scenes"].attributes.options
const scenesTinaDesk = states["input_select.tina_desk_scenes"].attributes.options
const scenesDiningRoom = states["input_select.dining_room_lamp_scenes"].attributes.options
const definedLivingRoom = scenesLivingRoom.includes(currentSceneLivingRoom)
const definedBasement = scenesBasement.includes(currentSceneBasement)
const definedTinaDesk = scenesTinaDesk.includes(currentSceneTinaDesk)
const definedDiningRoom = scenesDiningRoom.includes(currentSceneDiningRoom)
// Define variables
let setSceneLivingRoom = []
let setSceneTinaDesk = []
let setSceneBasement = []
let setSceneDiningRoom = []
let setServiceLivingRoom = []
let setServiceTinaDesk = []
let setServiceDiningRoom = []
let setServiceBasement = []
let restoreLivingRoom = {}
let restoreTinaDesk = {}
let restoreBasement = {}
// Decide what to restore for each room
if (stateLivingRoom === 'on') {
setServiceLivingRoom = 'turn_on'
if (definedLivingRoom === true) {
restoreLivingRoom = 'defined'
setSceneLivingRoom = currentSceneLivingRoom
} else if (nightLivingRoom === 'on') {
restoreLivingRoom = 'night'
setSceneLivingRoom = 'Adaptive'
} else if (adaptLivingRoom === 'on') {
restoreLivingRoom = 'adaptive'
setSceneLivingRoom = 'Adaptive'
} else {
restoreLivingRoom = 'backup'
setSceneLivingRoom = 'living_room_alert_restore'
}
} else {
restoreLivingRoom = 'off'
setServiceLivingRoom = 'turn_off'
setSceneLivingRoom = 'Adaptive'
}
if (stateTinaDesk === 'on') {
setServiceTinaDesk = 'turn_on'
if (definedTinaDesk === true) {
restoreTinaDesk = 'defined'
setSceneTinaDesk = currentSceneTinaDesk
} else {
restoreTinaDesk = 'adaptive'
setSceneTinaDesk = 'Reset'
}
} else {
restoreTinaDesk = 'off'
setServiceTinaDesk = 'turn_off'
}
if (stateDiningRoom === 'on') {
setServiceDiningRoom = 'turn_on'
if (nightDiningRoom === 'on') {
setSceneDiningRoom = 'Nightlight'
} else {
setSceneDiningRoom = 'Adaptive'
}
} else {
setServiceDiningRoom = 'turn_off'
}
if (stateBasement === 'on') {
setServiceBasement = 'turn_on'
if (definedBasement === true) {
restoreBasement = 'defined'
setSceneBasement = currentSceneBasement
} else if (nightBasement === 'on') {
restoreBasement = 'night'
setSceneBasement = 'Adaptive'
} else if (adaptBasement === 'on') {
restoreBasement = 'adaptive'
setSceneBasement = 'Adaptive'
} else {
restoreBasement = 'backup'
setSceneBasement = 'basement_alert_restore'
}
} else {
restoreBasement = 'off'
setServiceBasement = 'turn_off'
setSceneBasement = 'Adaptive'
}
// Define messages
let sendSceneLivingRoom = {
"payload": {
"domain": "input_select",
"service": "select_option",
"target": {
"entity_id": ["input_select.living_room_scenes"]
},
"data": {
"option": setSceneLivingRoom
}
}
}
let sendSceneTinaDesk = {
"payload": {
"domain": "input_select",
"service": "select_option",
"target": {
"entity_id": ["input_select.tina_desk_scenes"]
},
"data": {
"option": setSceneTinaDesk
}
}
}
let sendSceneBasement = {
"payload": {
"domain": "input_select",
"service": "select_option",
"target": {
"entity_id": ["input_select.basement_studio_scenes"]
},
"data": {
"option": setSceneBasement
}
}
}
let sendSceneDiningRoomLamp = {
"payload": {
"domain": "input_select",
"service": "select_option",
"target": {
"entity_id": ["input_select.dining_room_lamp_scenes"]
},
"data": {
"option": setSceneDiningRoom
}
}
}
let sendNightLivingRoom = {
"payload": {
"domain": "switch",
"service": "turn_on",
"target": {
"entity_id": ["switch.adaptive_lighting_sleep_mode_living_room"]
},
"data": {}
}
}
let sendNightTinaDesk = {
"payload": {
"domain": "switch",
"service": "turn_on",
"target": {
"entity_id": ["switch.adaptive_lighting_sleep_mode_tina_desk"]
},
"data": {}
}
}
let sendNightBasement = {
"payload": {
"domain": "switch",
"service": "turn_on",
"target": {
"entity_id": ["switch.adaptive_lighting_sleep_mode_basement_studio"]
},
"data": {}
}
}
let sendStateLivingRoom = {
"payload": {
"domain": "light",
"service": setServiceLivingRoom,
"target": {
"entity_id": ["light.living_room_lights"]
},
"data": {}
}
}
let sendStateTinaDesk = {
"payload": {
"domain": "light",
"service": setServiceTinaDesk,
"target": {
"entity_id": ["light.tina_desk_lights"]
},
"data": {}
}
}
let sendStateDiningRoom = {
"payload": {
"domain": "light",
"service": setServiceDiningRoom,
"target": {
"entity_id": ["light.dining_room_lamp"]
},
"data": {}
}
}
let sendStateBasement = {
"payload": {
"domain": "light",
"service": setServiceBasement,
"target": {
"entity_id": ["light.basement_studio_lights"]
},
"data": {}
}
}
let sendRestoreLivingRoom = {
"payload": {
"domain": "scene",
"service": "turn_on",
"target": {
"entity_id": setSceneLivingRoom
},
"data": {}
}
}
let sendRestoreTinaDesk = {
"payload": {
"domain": "scene",
"service": "turn_on",
"target": {
"entity_id": setSceneTinaDesk
},
"data": {}
}
}
let sendRestoreBasement = {
"payload": {
"domain": "scene",
"service": "turn_on",
"target": {
"entity_id": setSceneBasement
},
"data": {}
}
}
let sendStairwellScript = {
"payload": {
"domain": "script",
"service": "stairwell_led_strip",
"data": {}
}
}
let sendStairwellOff = {
"payload": {
"domain": "light",
"service": "turn_off",
"target": {
"entity_id": ["light.stairwell_led_strip"]
},
"data": {}
}
}
// Send messages
if (restoreLivingRoom === 'backup') {
node.send([sendRestoreLivingRoom,null,null,null])
} else if (restoreLivingRoom === 'off') {
node.send([sendStateLivingRoom,null,null,null])
} else {
node.send([sendSceneLivingRoom,null,null,null])
}
if (restoreTinaDesk === 'backup') {
node.send([null,sendRestoreTinaDesk,null,null])
} else if (restoreTinaDesk === 'off') {
node.send([null,sendStateTinaDesk,null,null])
} else {
node.send([null,sendSceneTinaDesk,null,null])
}
node.send([null,null,[sendStateDiningRoom,sendSceneDiningRoomLamp],null])
if (restoreBasement === 'backup') {
node.send([null,null,null,sendRestoreBasement])
} else if (restoreBasement === 'off') {
node.send([null,null,null,sendStateBasement])
} else {
node.send([null,null,null,sendSceneBasement])
}
if (stateStairwell === 'on') {
node.send([sendStairwellScript,null,null,null])
} else {
node.send([sendStairwellOff,null,null,null])
}
flow.set('alertFirstFloor', false)
flow.set('alertBasement', false)
node.status({fill:'green',shape:'dot',text:'Scenes Reset'})

View File

@ -5,6 +5,9 @@ const tinawork = msg.tinawork
if (tinawork === 'on' && workEndZone === 'on') {
node.status({fill:"green",shape:"dot",text:"Activated"})
return[msg,null]
} else if (tinawork === 'on' && workEndZone === 'off') {
node.status({fill:"yellow",shape:"dot",text:"Not time to leave yet"})
return null
} else if (tinawork === 'off') {
node.status({fill:"yellow",shape:"dot",text:"No Work Today"})
return null

View File

@ -0,0 +1,94 @@
const states = global.get('homeassistant.homeAssistant.states')
const masterBedroomSleeping = states['input_boolean.master_bedroom_sleeping'].state
const emmaSleep = states['input_boolean.emma_sleeping'].state
const tonyAwake = states['input_boolean.tony_awake'].state
const xiaDesktopUser = states['sensor.xia_desktop_current_username'].state
const xiaDesktopIdle = states['binary_sensor.tina_desktop_idle'].state
const tonyLocation = states['person.tony_stork'].state
const tinaLocation = states['person.christina_stork'].state
const overnight = states['binary_sensor.overnight'].state
const payload = msg.payload
const topic = msg.topic
// Set some default values
let who = 'Alexa Everywhere' // Default to playing on all Echo Dots unless told otherwise
let tinaAvailable = false
let tonyAvailable = false
let enable = false
let reason = {}
// Determine who is available to respond
if (tonyAwake === 'on' && tonyLocation === 'home') {
tonyAvailable = true
}
if (xiaDesktopUser === 'irish' && xiaDesktopIdle === 'off' && tinaLocation === 'home') {
tinaAvailable = true
}
// If both of us are still asleep, or I am asleep and she is at work, play the alert only in the master bedroom
if (tinaAvailable === false && tonyAvailable === false) {
who = 'Master Bedroom'
}
// Determine if the alert should be sent
if (emmaSleep === 'on') {
if (overnight === 'on' && masterBedroomSleeping === 'on') {
enable = true
reason = 'Overnight is on, and master bedroom is sleeping'
} else if (overnight === 'off') {
enable = true
reason = 'Overnight is off'
}
}
// Define the alert message
let alertTTS = {
"topic": who,
"payload": "Emma has opened her door, please check on her. I repeat, Emma has opened her door, please check on her. This is urgent, Emma is awake. PDS toddler warning, a large and extremely dangerous toddler is on the ground in your area.",
"type": "critical"
}
// Add some logs for debugging
node.log(`--------------- Emma Door Alert --------------`)
node.log(`Emma Door Alert - topic: ${topic}`)
node.log(`Emma Door Alert - payload: ${payload}`)
node.log(`Emma Door Alert - masterBedroomSleeping: ${masterBedroomSleeping}`)
node.log(`Emma Door Alert - emmaSleep: ${emmaSleep}`)
node.log(`Emma Door Alert - tonyAwake: ${tonyAwake}`)
node.log(`Emma Door Alert - xiaDesktopUser: ${xiaDesktopUser}`)
node.log(`Emma Door Alert - xiaDesktopIdle: ${xiaDesktopIdle}`)
node.log(`Emma Door Alert - tonyLocation: ${tonyLocation}`)
node.log(`Emma Door Alert - tinaLocation: ${tinaLocation}`)
node.log(`Emma Door Alert - overnight: ${overnight}`)
node.log(`Emma Door Alert - tonyAvailable: ${tonyAvailable}`)
node.log(`Emma Door Alert - tinaAvailable: ${tinaAvailable}`)
node.log(`Emma Door Alert - who: ${who}`)
node.log(`Emma Door Alert - enable: ${enable}`)
node.log(`Emma Door Alert - reason: ${reason}`)
node.log(`----------------------------------------------`)
// Send the alert if conditions are met
if (topic === 'door' && payload === 'on') {
if (enable === true) {
node.status({fill:'green',shape:'dot',text:`Sending alert to ${who}`})
node.send([alertTTS,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Door Opened, but no alert sent'})
}
} else if (topic === 'timer') {
node.status({fill:'green',shape:'dot',text:'Timer Finished'})
node.send([alertTTS,msg,null])
} else if (topic === 'cancel') {
node.status({fill:'green',shape:'dot',text:'Cancel Button Pressed'})
node.send([null,null,msg])
} else if (topic === 'door' && payload === 'off') {
node.status({fill:'green',shape:'dot',text:'Door Closed'})
}

View File

@ -9,17 +9,22 @@ const newDuration = duration * 60
if (sleeping === 'off') {
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -3,23 +3,30 @@ const lights = states['light.kallen_bedroom_lights'].state
const duration = states['input_number.kallen_bedroom_lights_off_delay'].state
const lux = parseInt(states['sensor.kallen_bedroom_illuminance'].state)
const threshold = parseInt(states['input_number.kallen_bedroom_lux_threshold'].state)
const sleeping = states['input_boolean.kallen_sleeping'].state
const kallenBedroomSleep = states['input_boolean.kallen_sleeping'].state
const masterBedroomSleep = states['input_boolean.master_bedroom_sleeping'].state
const peopleSleeping = (masterBedroomSleep === 'on' || kallenBedroomSleep === 'on')
const payload = msg.payload
const newDuration = duration * 60
if (sleeping === 'off') {
if (peopleSleeping === false) {
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

View File

@ -12,17 +12,22 @@ const newDuration = duration * 60
if (peopleSleeping === false && nightMode === 'off') {
if (payload === 'on') {
if (lux <= threshold || lights === 'on') {
node.status({fill:'green',shape:'dot',text:'Lights On'})
node.send([msg,null])
if (lux <= threshold && lights === 'off') {
node.status({fill:'green',shape:'dot',text:'Turning lights on'})
node.send([msg,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
if (lights === 'on') {
node.status({fill:'red',shape:'ring',text:'Lights already on'})
node.send([null,msg,null])
} else {
node.status({fill:'red',shape:'ring',text:'Too bright'})
}
}
} else if (payload === 'off') {
if (lights === 'on') {
msg.duration = newDuration
node.status({fill:"green",shape:"dot",text:parseInt(duration) + ' minutes'})
node.send([null,msg])
node.send([null,null,msg])
} else {
node.status({fill:"red",shape:"ring",text:"Lights already off"})
}

50
subflows/speech-engine.js Normal file
View File

@ -0,0 +1,50 @@
const livingRoom = ["Living Room", "Living Room Echo Dot"]
const basementAlexa = ["Basement", "Basement Echo Dot"]
const basementGoogle = ["Basement Google", "basement_google"]
const kallenBedroom = ["Kallen Bedroom", "Kallen Bedroom Speaker"]
const emmaBedroom = ["Emma Bedroom", "Emma Bedroom Speaker"]
const kidsBedrooms = ["Kids Bedrooms"]
const allBedrooms = ["All Bedrooms"]
const masterBedroom = ["Master Bedroom", "Master Bedroom Echo Dot"]
const commonAreas = ["Common Areas"]
const alexaEverywhere = ["alexa_everywhere", "Alexa Everywhere"]
const room = msg.topic
const voice = msg.voice || global.get("jarvis_voice", "diskCon")
const type = msg.type || "normal"
if (livingRoom.includes(room)) {
msg.topic = "living_room"
} else if (basementAlexa.includes(room)) {
msg.topic = "basement"
} else if (basementGoogle.includes(room)) {
msg.topic = "basement_google"
} else if (kallenBedroom.includes(room)) {
msg.topic = "kallen_bedroom"
} else if (emmaBedroom.includes(room)) {
msg.topic = "emma_bedroom"
} else if (kidsBedrooms.includes(room)) {
msg.topic = "kids_bedrooms"
} else if (allBedrooms.includes(room)) {
msg.topic = "all_bedrooms"
} else if (masterBedroom.includes(room)) {
msg.topic = "master_bedroom"
} else if (commonAreas.includes(room)) {
msg.topic = "common"
} else if (alexaEverywhere.includes(room)) {
msg.topic = "alexa_everywhere"
} else if (room === "Everywhere") {
msg.topic = "everywhere"
}
msg.voice = voice
msg.type = type
let statusMsg = {
"status": {
"fill": "green",
"shape": "dot",
"text": `${msg.voice} speaking in ${room}`
}
}
node.send([msg,statusMsg])

View File

@ -0,0 +1,36 @@
// Get the user ID list from global context
const users = global.get("userIDList", "diskCon") || {}
// Initialize variables
let userTrigger = "none"
let statusTxt = ""
// Extract user ID from the message
if (msg.payload?.context?.user_id) {
userTrigger = msg.payload.context.user_id
} else if (msg.data?.context?.user_id) {
userTrigger = msg.data.context.user_id
}
// Validate userTrigger and set status message
if (userTrigger === "none" || !users[userTrigger]) {
statusTxt = "User not found"
node.error(`User ID '${userTrigger}' not found in user list.`)
return null // Stop execution if user is not found
} else {
const user = users[userTrigger]
statusTxt = `User: ${user.firstName}`
msg.user = { ...user } // Avoid mutating the original msg
}
// Create the status message
const statusMsg = {
status: {
fill: "green",
shape: "dot",
text: `${statusTxt} at ${new Date().toLocaleString()}`
}
}
// Send the message and status message to the output nodes
node.send([msg, statusMsg])

22
subflows/user-id-list.js Normal file
View File

@ -0,0 +1,22 @@
let userIDList = {
"c3909d27048140729f002aaef0391775": {
"shortName": "tony",
"firstName": "Tony",
"lastName": "Stork",
"hassUser": "tonystork"
},
"f387a983651a4321a7411ff8cf36f949": {
"shortName": "tina",
"firstName": "Christina",
"lastName": "Stork",
"hassUser": "christinastork"
},
"879b4c04e32841b3ad1d2763a11b4e70": {
"shortName": "kallen",
"firstName": "Kallen",
"lastName": "Stork",
"hassUser": "kallenstork"
}
}
global.set("userIDList",userIDList,"diskCon")

View File

@ -127,8 +127,7 @@ node.log("Time-based Automations: Decision Logic Complete")
// ---------- Service Calls ----------
let sendLights = {
"payload": {
"domain": "light",
"service": "turn_off",
"action": "light.turn_off",
"target": {
"entity_id": lightsOff
},
@ -138,8 +137,7 @@ let sendLights = {
let sendSleepOff = {
"payload": {
"domain": "switch",
"service": "turn_off",
"action": "switch.turn_off",
"target": {
"entity_id": adaptiveSleep
},
@ -149,8 +147,7 @@ let sendSleepOff = {
let sendBooleanOff = {
"payload": {
"domain": "input_boolean",
"service": "turn_off",
"action": "input_boolean.turn_off",
"target": {
"entity_id": booleanOff
},
@ -160,8 +157,7 @@ let sendBooleanOff = {
let sendAdaptive = {
"payload": {
"domain": "switch",
"service": "turn_" + setAdaptive,
"action": "switch.turn_" + setAdaptive,
"target": {
"entity_id": switchAdaptive
},
@ -171,8 +167,7 @@ let sendAdaptive = {
let sendSceneResetMain = {
"payload": {
"domain": "input_text",
"service": "set_value",
"action": "input_text.set_value",
"target": {
"entity_id": selScenesMain
},
@ -184,8 +179,7 @@ let sendSceneResetMain = {
let sendSceneResetDesk = {
"payload": {
"domain": "input_text",
"service": "set_value",
"action": "input_text.set_value",
"target": {
"entity_id": selScenesTinaDesk
},
@ -197,8 +191,7 @@ let sendSceneResetDesk = {
let sendHoliday = {
"payload": {
"domain": "switch",
"service": "turn_" + setHoliday,
"action": "switch.turn_" + setHoliday,
"target": {
"entity_id": switchHoliday
},
@ -208,8 +201,7 @@ let sendHoliday = {
let sendTimer = {
"payload": {
"domain": "timer",
"service": "start",
"action": "timer.start",
"target": {
"entity_id": timerEntity
},
@ -221,8 +213,7 @@ let sendTimer = {
let sendTimerCancel = {
"payload": {
"domain": "timer",
"service": "cancel",
"action": "timer.cancel",
"target": {
"entity_id": timerEntity
},
@ -232,8 +223,7 @@ let sendTimerCancel = {
let sendNotifyPhone = {
"payload": {
"domain": "script",
"service": "text_notify",
"action": "script.text_notify",
"data": {
"who": "all",
"title": notifyTitle,
@ -246,8 +236,7 @@ let sendNotifyPhone = {
let sendNotifyTV = {
"payload": {
"domain": "script",
"service": "tv_notify",
"action": "script.tv_notify",
"data": {
"who": "all",
"title": notifyTitle,
@ -263,8 +252,7 @@ let sendNotifyTV = {
let sendFirstFloorScene = {
"payload": {
"domain": "script",
"service": "evening_on_first_floor",
"action": "script.evening_on_first_floor",
"data": {
"sunset_lights": 1
}
@ -273,8 +261,7 @@ let sendFirstFloorScene = {
let sendSecondFloorScene = {
"payload": {
"domain": "script",
"service": "evening_on_second_floor",
"action": "script.evening_on_second_floor",
"data": {
"sunset_lights": 1
}
@ -283,8 +270,7 @@ let sendSecondFloorScene = {
let sendDeskScene = {
"payload": {
"domain": "input_select",
"service": "select_option",
"action": "input_select.select_option",
"target": {
"entity_id": ["input_select.tina_desk_scenes"]
},

View File

@ -0,0 +1,44 @@
let tomorrow = msg.tomorrow
let today = msg.today
let number = {}
let work_tomorrow = {}
// Check if calendar data exists
if (!msg.payload || msg.payload.length === 0) {
msg.payload = [{
date: "No Data",
eventStart: "No Data",
eventEnd: "No Data",
summary: "No Data",
location: "No Data",
isRecurring: "No Data",
allDay: "No Data",
calendarName: "No Data",
countdown: "No Data",
uid: { date: "No Data" }
}]
work_tomorrow = "false"
number = 0
} else {
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
node.status({fill:"green",shape:"dot",text:`Number ${number}`})
if (number == 0) {
node.send([msg,null])
} else {
node.send([null,msg])
}

69
weather/alerts_filter.js Normal file
View File

@ -0,0 +1,69 @@
const severeWarningEvents = ["Severe Thunderstorm Warning", "Destructive Severe Thunderstorm Warning", "Considerable Destructive Severe Thunderstorm Warning"]
const tornadoWarningEvents = ["Tornado Warning", "Radar Indicated Tornado Warning", "Confirmed Tornado Warning", "Tornado Emergency"]
let alerts = msg.payload
// Helper function to safely access nested properties
const getNestedProperty = (obj, path, defaultValue = undefined) => {
return path.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : defaultValue), obj)
}
// Filter function
const filterAlerts = (alerts, condition) => {
return alerts.filter(alert => {
try {
return condition(alert)
} catch (e) {
node.warn(`Error processing alert: ${e.message}`)
return false
}
})
}
// Filtering logic
const ts = filterAlerts(alerts, alert => {
const event = getNestedProperty(alert, ['raw', 'properties', 'event'])
return severeWarningEvents.includes(event)
})
const tstp = filterAlerts(alerts, alert => {
const event = getNestedProperty(alert, ['raw', 'properties', 'event'])
const tornadoDetection = getNestedProperty(alert, ['raw', 'properties', 'parameters', 'tornadoDetection'], [])
return severeWarningEvents.includes(event) && tornadoDetection.includes("POSSIBLE")
})
const cdst = filterAlerts(alerts, alert => {
const event = getNestedProperty(alert, ['raw', 'properties', 'event'])
return event === "Considerable Destructive Severe Thunderstorm Warning"
})
const tornado = filterAlerts(alerts, alert => {
const event = getNestedProperty(alert, ['raw', 'properties', 'event'])
return tornadoWarningEvents.includes(event)
})
const confirmed_tornado = filterAlerts(alerts, alert => {
const event = getNestedProperty(alert, ['raw', 'properties', 'event'])
return event === "Confirmed Tornado Warning"
})
// Output messages
let tstormMsg = {
"payload": {
"alerts": ts,
"count": ts.length,
"tornado_possible": tstp.length,
"considerable_destructive": cdst.length
}
}
let tornadoWarnMsg = {
"payload": {
"alerts": tornado,
"count": tornado.length,
"confirmed": confirmed_tornado.length
}
}
node.send([tstormMsg, tornadoWarnMsg])
node.status({ fill: 'green', shape: 'dot', text: `${alerts.length} alerts processed at ${new Date().toLocaleString()}`})

150
weather/local_alerts.js Normal file
View File

@ -0,0 +1,150 @@
const severeWarningEvents = ["Severe Thunderstorm Warning","Destructive Severe Thunderstorm Warning","Considerable Destructive Severe Thunderstorm Warning"]
const tornadoWarningEvents = ["Tornado Warning","Radar Indicated Tornado Warning","Confirmed Tornado Warning","Tornado Emergency"]
const tornadoWatchEvents = ["Tornado Watch"]
const severeThunderstormWatchEvents = ["Severe Thunderstorm Watch"]
const area = msg.area
const mqttTopic = "weather/alerts/" + area
const allTopic = mqttTopic + "/alerts"
const tstormTopic = mqttTopic + "/severe_thunderstorm_warning"
const tornadoWarnTopic = mqttTopic + "/tornado_warning"
const tornadoWatchTopic = mqttTopic + "/tornado_watch"
const severeThunderstormWatchTopic = mqttTopic + "/severe_thunderstorm_watch"
let alerts = msg.payload.features
let tornado_possible = false
let considerable_destructive = false
let confirmed = false
// Filter for Severe Thunderstorm Warnings
let ts = alerts.filter(function(alert) {
if (alert.properties &&
severeWarningEvents.includes(alert.properties.event)) {
return true
}
})
// Filter for Severe Thunderstorm Warnings with tornado detection
// that have a tornado possible parameter
let tstp = alerts.filter(function(alert) {
if (alert.properties &&
severeWarningEvents.includes(alert.properties.event) &&
alert.properties.parameters &&
alert.properties.parameters.tornadoDetection &&
alert.properties.parameters.tornadoDetection.length > 0) {
let tornadoPossible = alert.properties.parameters.tornadoDetection[0]
return tornadoPossible === "POSSIBLE"
}
})
if (tstp.length > 0) {
tornado_possible = true
}
// Filter for considerable destructive severe thunderstorm warnings
let cdst = alerts.filter(function(alert) {
if (alert.properties &&
alert.properties.event === "Considerable Destructive Severe Thunderstorm Warning") {
return true
}
})
if (cdst.length > 0) {
considerable_destructive = true
}
// Filter for Tornado Warnings
let tornado = alerts.filter(function(alert) {
if (alert.properties &&
tornadoWarningEvents.includes(alert.properties.event)) {
return true
}
})
let confirmed_tornado = alerts.filter(function(alert) {
if (alert.properties &&
alert.properties.event === "Confirmed Tornado Warning") {
return true
}
})
if (confirmed_tornado.length > 0) {
confirmed = true
}
// Filter for Tornado Watches
let tornado_watch = alerts.filter(function(alert) {
if (alert.properties &&
tornadoWatchEvents.includes(alert.properties.event)) {
return true
}
})
// Filter for Severe Thunderstorm Watches
let severe_thunderstorm_watch = alerts.filter(function(alert) {
if (alert.properties &&
severeThunderstormWatchEvents.includes(alert.properties.event)) {
return true
}
})
// If there are any alerts, return them
let allAlerts = {
"payload": msg.payload.features,
"topic": allTopic
}
let tstormMsg = {
"payload": {
"alerts": ts,
"count": ts.length,
"tornado_possible": tornado_possible,
"considerable_destructive": considerable_destructive
},
"topic": tstormTopic
}
let tornadoWarnMsg = {
"payload": {
"alerts": tornado,
"count": tornado.length,
"confirmed": confirmed
},
"topic": tornadoWarnTopic
}
let tornadoWatchMsg = {
"payload": {
"alerts": tornado_watch,
"count": tornado_watch.length
},
"topic": tornadoWatchTopic
}
let severeThunderstormWatchMsg = {
"payload": {
"alerts": severe_thunderstorm_watch,
"count": severe_thunderstorm_watch.length
},
"topic": severeThunderstormWatchTopic
}
// Create status message for node
let statusMsg = {
"status": {
"fill": "green",
"shape": "dot",
"text": `${alerts.length} alerts processed at ${new Date().toLocaleString()}`
}
}
// Send messages to output nodes
node.send([allAlerts,tstormMsg,tornadoWarnMsg,tornadoWatchMsg,severeThunderstormWatchMsg,statusMsg])