From 7bf25d79f4325a738b3ae42a10be4d3c632c495b Mon Sep 17 00:00:00 2001 From: Tony Stork Date: Fri, 20 Mar 2026 22:52:19 -0400 Subject: [PATCH] Disable E's climate watchdog if aircon not in use home_automation/Home-Assistant-Configs#265 --- .gitignore | 1 - README.md | 6 ------ flows.json | 2 +- flows_cred.json | 3 --- package.json | 25 ------------------------- 5 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 .gitignore delete mode 100644 README.md delete mode 100644 flows_cred.json delete mode 100644 package.json diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3c9c01a..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.backup \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index a62af8a..0000000 --- a/README.md +++ /dev/null @@ -1,6 +0,0 @@ -NerdFlows -========= - -Version control for my Node-RED flows. The main repository can be found below. - -- [Home Assistant Configs](https://github.com/tm24fan8/Home-Assistant-Configs) diff --git a/flows.json b/flows.json index d3c2734..41093d4 100644 --- a/flows.json +++ b/flows.json @@ -24930,7 +24930,7 @@ "z": "72f99805df043603", "g": "5802ea32c1de36e8", "name": "Watchdog", - "func": "const states = global.get('homeassistant.homeAssistant.states')\nconst sleeping = states['input_boolean.emma_sleeping'].state\n\nif (sleeping !== 'on') {\n node.status({fill:'red',shape:'ring',text:'Emma not sleeping, watchdog disabled'})\n return null\n}\n\nconst airconEntity = ['climate.emma_bedroom_aircon']\n\n// Gather relevant attributes from the aircon entity\n\nconst airconState = msg.payload.state\nconst airconAttributes = msg.payload.attributes\nconst airconEco = airconAttributes['eco_mode']\nconst airconTargetTemp = airconAttributes['temperature']\nconst airconFanMode = airconAttributes['fan_mode']\nconst airconDisplay = airconAttributes['screen_display']\n\n// Gather the last explicitly sent configuration from the context\n\nconst airconHvacCfg = flow.get('emmaBedroom.airconHvacMode', 'diskCon')\nconst airconEcoCfg = flow.get('emmaBedroom.airconEco', 'diskCon')\nconst airconTargetTempCfg = parseFloat(flow.get('emmaBedroom.airconTargetTemp', 'diskCon'))\nconst airconFanModeCfg = flow.get('emmaBedroom.airconFanMode', 'diskCon')\nconst airconDisplayCfg = flow.get('emmaBedroom.airconDisplay', 'diskCon')\n\n// Compare the current state with the last sent configuration\n\nlet ecoMatch = {}\nlet displayMatch = {}\nlet counter = 0\n\nlet sendDisplay = {\n \"payload\": {\n \"action\": `switch.${airconDisplayCfg}`,\n \"target\": {\n \"entity_id\": [\"switch.emma_bedroom_aircon_display\"]\n },\n \"data\": {}\n }\n}\n\nlet sendHvac = {\n \"payload\": {\n \"action\": \"climate.set_hvac_mode\",\n \"target\": {\n \"entity_id\": airconEntity\n },\n \"data\": {\n \"hvac_mode\": airconHvacCfg\n }\n }\n}\n\nlet sendTemp = {\n \"payload\": {\n \"action\": \"climate.set_temperature\",\n \"target\": {\n \"entity_id\": airconEntity\n },\n \"data\": {\n \"temperature\": airconTargetTempCfg\n }\n }\n}\n\nlet sendEco = {\n \"payload\": {\n \"action\": `switch.${airconEcoCfg}`,\n \"target\": {\n \"entity_id\": [\"switch.emma_bedroom_aircon_eco_mode\"]\n },\n \"data\": {}\n }\n}\n\nlet sendAcFan = {\n \"payload\": {\n \"action\": \"climate.set_fan_mode\",\n \"target\": {\n \"entity_id\": airconEntity\n },\n \"data\": {\n \"fan_mode\": airconFanModeCfg\n }\n }\n}\n\n// Fix hvac mode if mismatch\n\nif (airconState != airconHvacCfg) {\n node.log(`Emma Bedroom Aircon HVAC mode mismatch: ${airconState} vs ${airconHvacCfg}`)\n node.send(sendHvac)\n counter += 1\n}\n\n// Fix eco mode if mismatch\n\nif (airconEco === false && airconEcoCfg === 'turn_off') {\n ecoMatch = true\n} else if (airconEco === true && airconEcoCfg === 'turn_on') {\n ecoMatch = true\n} else {\n ecoMatch = false\n}\n\nif (ecoMatch === false) {\n node.log(`Emma Bedroom Aircon Eco mode mismatch: ${airconEco} vs ${airconEcoCfg}`)\n node.send(sendEco)\n counter += 1\n}\n\n// Fix target temperature if mismatch\n\nif (airconTargetTemp != airconTargetTempCfg) {\n node.log(`Target temperature mismatch: ${airconTargetTemp} vs ${airconTargetTempCfg}`)\n node.send(sendTemp)\n counter += 1\n}\n\n// Fix fan mode if mismatch\n\nif (airconFanMode != airconFanModeCfg) {\n node.log(`Fan mode mismatch: ${airconFanMode} vs ${airconFanModeCfg}`)\n node.send(sendAcFan)\n counter += 1\n}\n\n// Fix display mode if mismatch\n\nif (airconDisplay === false && airconDisplayCfg === 'turn_off') {\n displayMatch = true\n} else if (airconDisplay === true && airconDisplayCfg === 'turn_on') {\n displayMatch = true\n} else {\n displayMatch = false\n}\n\nif (displayMatch === false) {\n node.log(`Emma Bedroom Aircon Display mode mismatch: ${airconDisplay} vs ${airconDisplayCfg}`)\n node.send(sendDisplay)\n counter += 1\n}\n\nif (counter > 0) {\n node.log(`Emma Bedroom Aircon: ${counter} configuration(s) fixed`)\n node.status({fill:'yellow',shape:'dot',text:`${counter} config(s) fixed`})\n} else {\n node.status({fill:'green',shape:'dot',text:`No config changes`})\n}", + "func": "const states = global.get('homeassistant.homeAssistant.states')\n\n// Check if all settings support aircon usage\n\nconst ac = global.get('emmaBedroom.aircon.installed', \"diskCon\")\nconst allowed = states['input_boolean.emma_bedroom_climate_protocol'].state\nconst schedMode = states['input_select.scheduled_climate_mode_emma_bedroom'].state\nconst coolingActive = states[\"input_boolean.emma_bedroom_cooling_on\"].state\nconst sleeping = states[\"input_boolean.emma_sleeping\"].state\n\nlet proceed = false\n\nif (ac === true && allowed === 'on' && schedMode === 'AC' && coolingActive === 'on') {\n proceed = true\n}\n\n// \n\nif (sleeping != 'on') {\n node.status({fill:'red',shape:'ring',text:'Emma not sleeping, watchdog disabled'})\n return null\n} else if (proceed === false) {\n node.status({fill:'red',shape:'ring',text:'Aircon not in use, watchdog disabled'})\n return null\n}\n\nconst airconEntity = ['climate.emma_bedroom_aircon']\n\n// Gather relevant attributes from the aircon entity\n\nconst airconState = msg.payload.state\nconst airconAttributes = msg.payload.attributes\nconst airconEco = airconAttributes['eco_mode']\nconst airconTargetTemp = airconAttributes['temperature']\nconst airconFanMode = airconAttributes['fan_mode']\nconst airconDisplay = airconAttributes['screen_display']\n\n// Gather the last explicitly sent configuration from the context\n\nconst airconHvacCfg = flow.get('emmaBedroom.airconHvacMode', 'diskCon')\nconst airconEcoCfg = flow.get('emmaBedroom.airconEco', 'diskCon')\nconst airconTargetTempCfg = parseFloat(flow.get('emmaBedroom.airconTargetTemp', 'diskCon'))\nconst airconFanModeCfg = flow.get('emmaBedroom.airconFanMode', 'diskCon')\nconst airconDisplayCfg = flow.get('emmaBedroom.airconDisplay', 'diskCon')\n\n// Compare the current state with the last sent configuration\n\nlet ecoMatch = {}\nlet displayMatch = {}\nlet counter = 0\n\nlet sendDisplay = {\n \"payload\": {\n \"action\": `switch.${airconDisplayCfg}`,\n \"target\": {\n \"entity_id\": [\"switch.emma_bedroom_aircon_display\"]\n },\n \"data\": {}\n }\n}\n\nlet sendHvac = {\n \"payload\": {\n \"action\": \"climate.set_hvac_mode\",\n \"target\": {\n \"entity_id\": airconEntity\n },\n \"data\": {\n \"hvac_mode\": airconHvacCfg\n }\n }\n}\n\nlet sendTemp = {\n \"payload\": {\n \"action\": \"climate.set_temperature\",\n \"target\": {\n \"entity_id\": airconEntity\n },\n \"data\": {\n \"temperature\": airconTargetTempCfg\n }\n }\n}\n\nlet sendEco = {\n \"payload\": {\n \"action\": `switch.${airconEcoCfg}`,\n \"target\": {\n \"entity_id\": [\"switch.emma_bedroom_aircon_eco_mode\"]\n },\n \"data\": {}\n }\n}\n\nlet sendAcFan = {\n \"payload\": {\n \"action\": \"climate.set_fan_mode\",\n \"target\": {\n \"entity_id\": airconEntity\n },\n \"data\": {\n \"fan_mode\": airconFanModeCfg\n }\n }\n}\n\n// Fix hvac mode if mismatch\n\nif (airconState != airconHvacCfg) {\n node.log(`Emma Bedroom Aircon HVAC mode mismatch: ${airconState} vs ${airconHvacCfg}`)\n node.send(sendHvac)\n counter += 1\n}\n\n// Fix eco mode if mismatch\n\nif (airconEco === false && airconEcoCfg === 'turn_off') {\n ecoMatch = true\n} else if (airconEco === true && airconEcoCfg === 'turn_on') {\n ecoMatch = true\n} else {\n ecoMatch = false\n}\n\nif (ecoMatch === false) {\n node.log(`Emma Bedroom Aircon Eco mode mismatch: ${airconEco} vs ${airconEcoCfg}`)\n node.send(sendEco)\n counter += 1\n}\n\n// Fix target temperature if mismatch\n\nif (airconTargetTemp != airconTargetTempCfg) {\n node.log(`Target temperature mismatch: ${airconTargetTemp} vs ${airconTargetTempCfg}`)\n node.send(sendTemp)\n counter += 1\n}\n\n// Fix fan mode if mismatch\n\nif (airconFanMode != airconFanModeCfg) {\n node.log(`Fan mode mismatch: ${airconFanMode} vs ${airconFanModeCfg}`)\n node.send(sendAcFan)\n counter += 1\n}\n\n// Fix display mode if mismatch\n\nif (airconDisplay === false && airconDisplayCfg === 'turn_off') {\n displayMatch = true\n} else if (airconDisplay === true && airconDisplayCfg === 'turn_on') {\n displayMatch = true\n} else {\n displayMatch = false\n}\n\nif (displayMatch === false) {\n node.log(`Emma Bedroom Aircon Display mode mismatch: ${airconDisplay} vs ${airconDisplayCfg}`)\n node.send(sendDisplay)\n counter += 1\n}\n\nif (counter > 0) {\n node.log(`Emma Bedroom Aircon: ${counter} configuration(s) fixed`)\n node.status({fill:'yellow',shape:'dot',text:`${counter} config(s) fixed`})\n} else {\n node.status({fill:'green',shape:'dot',text:`No config changes`})\n}", "outputs": 1, "timeout": 0, "noerr": 0, diff --git a/flows_cred.json b/flows_cred.json deleted file mode 100644 index 2b4c696..0000000 --- a/flows_cred.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$": "a87a61724068c56c49f3276823153f20t5tSa9XnzLgi3FLamTuZxFmhceGXBcMiyV/KEY75o87tzxSWBNEXe2c64EfUnpucuIGHiaj2ef0Tc/xDj0xUW+rALkxBCQOCl64tOcz7xxvFE5vngHXkMdz9UlsamUH5uFB8i+qnlrcw10KG3+bYWY37Ppg7+Vj22MIzEkZpc6olUqV64dlafypa73By6W0pcSO20IFjboFE7Ogyx6GleMVsE7ZoM1nzQJ3SKWOu4oWu6fYnLHbqDV/zmpO7H4FtuT8IQvUAx32kGJS11SAPRRJwGt20qNCsSF9E7iMIQWf7VPU5VR49K76ztRrxjhGqp2CYIIkj2guG9gjQsfdjDQGyKTQmbc/VEoCIAfV5p3YOZgqlAKyKKc9N2nvBgL3OhXq450LqBUWREwFV9fvR+lGgu2Tq5CmyUHPhzODq84wVtEZKAiawgzWzmx/OV9faxoqV0+ObfkKMWbN0NREIC5CvhhD7NCdC4GGVM4UBVP4naJ7qua4tqUEoqf3rmztV82AgSwX7SwEDcbOekSrcBOpmFcxYJ36XJLow9WB9QiSnrWQSlCJEj05PTNpTp6m5oDrr57cts15HdwDNHCv3B+y4Y+QMigbZnTgwPgUdpGLQ7Nl6+SPwzhpFxS/Ni2J8+ULSgg6BpJ+F+M8401Zk/oUGRnC+mywnejpE0wds8r3j96E=" -} \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index 8e44203..0000000 --- a/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "NerdFlows", - "description": "Version control for my Node-RED flows", - "version": "1.0.0", - "dependencies": { - "node-red-contrib-googlehome": "0.0.15", - "node-red-contrib-lifx-api": "1.2.1", - "node-red-contrib-stoptimer": "0.0.7", - "node-red-node-wol": "0.2.0", - "node-red-contrib-color-convert": "0.0.8", - "node-red-contrib-github-plus": "0.3.1", - "node-red-contrib-moment": "5.0.0", - "node-red-contrib-alexa-remote2-applestrudel": "5.0.50", - "node-red-contrib-discord-advanced": "3.6.0", - "node-red-contrib-home-assistant-websocket": "0.74.2", - "node-red-contrib-ical-events": "2.2.5", - "node-red-node-suncalc": "1.2.0" - }, - "node-red": { - "settings": { - "flowFile": "flows.json", - "credentialsFile": "flows_cred.json" - } - } -} \ No newline at end of file