15 minute read

This post is going to cover normalizing all media_player volume levels with Home Assistant and Node-RED. If you haven’t already, I’d recommend reading my previous blog post on sending Text-to-Speech notifications which dives a bit deeper on the optional accessibility Subflow this Flow consumes.

This automation normalizes all volume levels in my home based on the time of day using the Amazon Alexa Media Player Integration to bring in all my Amazon Echo and ecobee Switch+ devices into Home Assistant.

This is one of my favorite automations in my house and I think you will enjoy it too! I wrote this automation for several reasons:

  • I wanted all speakers to be quiet at bed time. This prevents someone from getting blasted at 2:00 am in the morning.
  • In the morning after I’ve had my cup of coffee, I want to increase the volume to an acceptable level.
  • Automation allows for reuse via sub-flows. This means you can trigger it for other use cases like friends are over and music is playing, increase or decrease the volume.
  • One less thing I have to think about, or constantly touch to change. In todays pandemic, this is also one less thing you potentially have to clean!
  • You can use this same routine for increasing the volume when triggering an alarm.
  • All speakers are at the same volume level when playing music on every speaker.

This Automation Flow is comprised of two different Subflows.

Alexa Media Player Custom Component

If you haven’t done so already, install the Alexa Media Player Custom Component if you are using Alexa, otherwise skip this section.

This addon is amazing as it normalizes all of your Alexa enabled devices such as the Amazon Echo or ecobee Switch+ into a media_player that can be used by Home Assistant.

Service to set volume level.

This Automation Flow controls the volume levels by calling the media_player.volume_set service. I’d recommend testing it first by opening the Home Assistant Developer tools and navigating to the Services tab.

Next, start playing some music on your media_player and call the media_player.volume_set service with a payload of:

entity_id: media_player.amazon_echo_plus
volume_level: 0.6

If you don’t hear a volume change, try a different volume_level in case the the device is already at that level. If it’s not then stop and check the logs. This will help diagnose why the device isn’t working and save you lots of time before continuing.

Input Boolean to control automations

I highly recommend adding multiple Input Booleans that controls if a specific automation or all automations can run. It’s always nice to be able to turn on or off a specific automation or all in the case of maintenance. Here are the three input_booleans that are used in this Automation Flow.

If you choose a different name, be sure to update the code below.

automation_enable:
  name: Enable Automations
  icon: mdi:home-automation

automation_normalize_volume:
  name: Automation - Automatically Normalize Volume throughout the day
  icon: mdi:volume-high

automation_notifications:
  name: Notify when an automation is triggered
  icon: mdi:home-automation

Set Volume Level Subflow

This Subflow will get the current devices volume level, compare the current and desired state. If changes are required, it will call the required service to change the devices volume level.

I then create a new Subflow with a status node, one input and one output. This Subflow optionally depends on the Send Automation Speech Notification Subflow.

graph TD InputNode(Input) --> ACurrentStateNode(fas:fa-database Get Current Volume Level) ACurrentStateNode --> BFunctionNode(fas:fa-code Set Volume Level Payload) BFunctionNode --> CCallServiceNode(fas:fa-play Set Volume Level) BFunctionNode --> DCallSubflowNode(fas:fa-sliders-h Send Automation Speech Notification) CCallServiceNode --> OutputNode(Output 1) StatusNode(fas:fa-heartbeat Status: All) --> StatusOutputNode(Status) style StatusNode fill:#ECF5FF style StatusOutputNode fill:#FAFAFB style InputNode fill:#FAFAFB style ACurrentStateNode fill:#66ACFD style BFunctionNode fill:#FBB68F style CCallServiceNode fill:#66ACFD style DCallSubflowNode fill:#FF75A1 style OutputNode fill:#FAFAFB linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4

Let’s break down the Subflow. The incoming message gets passed to the Get Current Volume Level Current State node to populate msg.data with the entity. This is then passed to the Set Volume Level Payload node which checks to see if any changes are required to meet the desired volume level and outputs Text-to-Speech (TTS) status messages. If changes are required, the Set Volume Level node will call the media_player.volume_set service.

Here is the JavaScript code contained in the Set Volume Level Payload Function node to smartly adjust the volume only if it is needed.

const entity = msg.payload && msg.payload.entity_id;
const attributes = msg.data && msg.data.attributes
if (!entity || !attributes) {
    node.status({ fill: "red", shape: "dot", text: "Invalid volume payload" });
    return [null, null];
}

const desired_volume_level = msg.payload.volume_level || 0;
if (desired_volume_level < 0.0 || desired_volume_level > 1.0) {
    let message = "Volume level must be between 0 and 1.";
    node.status({ fill: "red", shape: "dot", text: message });
    node.error(message);

    return [null, {
        payload: {
            entity_id: entity,
            message: message
        }
    }];
}

if (desired_volume_level === attributes.volume_level) {
    let message = "Volume level is already at the desired level.";
    node.status({ fill: "grey", shape: "dot", text: message });
    node.log(message);
    return [null, {
        payload: {
            entity_id: entity,
            message: message
        }
    }];
}

const volumePayload = {
    payload: {
        data: {
            entity_id: entity,
            volume_level: desired_volume_level
        }
    }
};

let message = "Setting Volume Level to " + (desired_volume_level * 100) + " percent.";
const speechPayload = {
    payload: {
        entity_id: entity,
        message: message
    }
};

node.status({ fill: "green", shape: "dot", text: message });
return [volumePayload, speechPayload];

As you an see it will output a friendly speech notification payload that will be sent to the Send Automation Speech Notification Subflow. If you are not using the alexa_media_player Home Assistant addon, you may want to update the you’ll want to update Set Volume Level Payload Function node to take in a volume range you are expecting.

You can trigger this Subflow by passing a message object with the following payload. I recommend using a Inject Node to test this out.

{
  "entity_id": "media_player.amazon_echo_plus",
  "volume_level":0.4
}

Please note, for my Alexa speakers. I use a volume level between the range of [0.0..0.10). To mute, pass 0.0.

Set Volume Level on All Media Devices Subflow

This subflow will get all media_player' and then send them one by one to the Set Volume Level Subflow.

If you are not using Alexa, you may need to tweak the Get All Media Players node as it’s returning all devices that have a specific feature attribute which says I support changing the volume level.

graph TD InputNode(Input) --> AFunctionNode(fas:fa-code Parse Volume Level) AFunctionNode --> BGetEntitiesNode(fas:fa-search Get All Media Players) BGetEntitiesNode --> CChangeNode(fas:fa-random Set Volume Level Payload) CChangeNode --> DSubflowNode(fas:fa-sliders-h Set Volume Level) DSubflowNode --> OutputNode(Output) StatusNode(fas:fa-heartbeat Status: All) --> StatusOutputNode(Status) style StatusNode fill:#ECF5FF style StatusOutputNode fill:#FAFAFB style InputNode fill:#FAFAFB style AFunctionNode fill:#FBB68F style BGetEntitiesNode fill:#66ACFD style CChangeNode fill:#FEA530 style DSubflowNode fill:#FF75A1 style OutputNode fill:#FAFAFB linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4

You can trigger this Subflow by passing a message object with the following float (e.g. 0.1) payload. I recommend using a Inject Node to test this out.

Normalize Volume Levels Everywhere Flow

This flow brings the two previous sub flows together to normalize all volume levels based on the time of the day. In my home, I set the level to 30% from 9:00am to 9:00pm, otherwise I set to 10%.

To have the flow set the volume levels at different times of the day, I use the BigTimer Node with an On Time of 09:00 and a Off Time of 21:00. Then I set the ON Msg to .3 and a OFF Msg to .1. When the timer turns on and turns off, it will check to see if my automation flags are turned on. If they are, it will enumerate over all media devices and set the volume based on the timers output message (e.g., .1 or .3).

graph TD ABigTimerNode(fas:fa-clock Normalize Volume) --> BCurrentStateNode(fas:fa-database Automations Enabled?) BCurrentStateNode --> CCurrentStateNode(fas:fa-database Normalize Volume Levels?) CCurrentStateNode --> DRbeNode(fas:fa-scroll Only allow changed values) DRbeNode --> ECallSubflow(fas:fa-sliders-h Set Volume Level on All Media Devices Subflow) StatusNode(fas:fa-heartbeat Status: All) --> StatusOutputNode(Status) style StatusNode fill:#ECF5FF style StatusOutputNode fill:#FAFAFB style ABigTimerNode fill:#3DB39F style BCurrentStateNode fill:#66ACFD style CCurrentStateNode fill:#66ACFD style DRbeNode fill:#FEB95E style ECallSubflow fill:#FF75A1 linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4

You can import this Flow and all Subflows shown above by importing the following JSON.

[
    {
        "id": "7a62a2d3.c0f4a4",
        "type": "Subflow",
        "name": "Send Automation Speech Notification",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 60,
                "y": 100,
                "wires": [
                    {
                        "id": "87411254.a6ed18"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 1120,
                "y": 100,
                "wires": [
                    {
                        "id": "d44cc51a.be0668",
                        "port": 0
                    }
                ]
            }
        ],
        "status": {
            "x": 220,
            "y": 40,
            "wires": [
                {
                    "id": "aef53056.742438",
                    "port": 0
                }
            ]
        }
    },
    {
        "id": "89300b1.595bcf8",
        "type": "function",
        "z": "7a62a2d3.c0f4a4",
        "name": "Set Speech Payload",
        "func": "const entity = flow.get(\"$parent.speech_entity_id\") || (msg.payload && msg.payload.entity_id) || \"media_player.office_echo_plus\";\nconst message = (msg.payload && msg.payload.message) || \"Automation provided no message\";\nconst announcement = msg.payload && msg.payload.announcement;\n\nif (announcement) {\n    node.status({ fill: \"green\", shape: \"dot\", text: \"Announce message:\" + message });\n    return {\n        payload:{\n            data: {\n                message: message,\n                data: { \"type\": \"announce\", \"method\": \"all\" },\n                target: !!entity ? [entity] : []\n            }\n        }\n    };\n}\n\nnode.status({ fill: \"green\", shape: \"dot\", text: \"TTS message:\" + message });\nreturn {\n    payload:{\n        data: {\n            message: message,\n            data: { type: \"tts\" },\n            target: [entity]\n        }\n    }\n};",
        "outputs": 1,
        "noerr": 0,
        "x": 700,
        "y": 100,
        "wires": [
            [
                "d44cc51a.be0668"
            ]
        ]
    },
    {
        "id": "d44cc51a.be0668",
        "type": "api-call-service",
        "z": "7a62a2d3.c0f4a4",
        "name": "Send Speech Notification",
        "server": "61956bd4.93df44",
        "version": 1,
        "debugenabled": false,
        "service_domain": "notify",
        "service": "alexa_media",
        "entityId": "",
        "data": "",
        "dataType": "json",
        "mergecontext": "",
        "output_location": "payload",
        "output_location_type": "msg",
        "mustacheAltTags": false,
        "x": 950,
        "y": 100,
        "wires": [
            []
        ]
    },
    {
        "id": "6add9bc6.4c3624",
        "type": "api-current-state",
        "z": "7a62a2d3.c0f4a4",
        "name": "Speech Notifications?",
        "server": "61956bd4.93df44",
        "version": 1,
        "outputs": 2,
        "halt_if": "true",
        "halt_if_type": "bool",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "input_boolean.automation_notifications",
        "state_type": "habool",
        "state_location": "",
        "override_payload": "none",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 440,
        "y": 140,
        "wires": [
            [
                "89300b1.595bcf8"
            ],
            []
        ],
        "outputLabels": [
            "",
            "enabled"
        ]
    },
    {
        "id": "aef53056.742438",
        "type": "status",
        "z": "7a62a2d3.c0f4a4",
        "name": "",
        "scope": null,
        "x": 100,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "87411254.a6ed18",
        "type": "function",
        "z": "7a62a2d3.c0f4a4",
        "name": "Check for overrides",
        "func": "const alwaysSpeak = msg.payload && msg.payload.announcement\nif (alwaysSpeak) {\n    return [msg, null];\n} else {\n    return [null, msg];\n}",
        "outputs": 2,
        "noerr": 0,
        "x": 210,
        "y": 100,
        "wires": [
            [
                "89300b1.595bcf8"
            ],
            [
                "6add9bc6.4c3624"
            ]
        ],
        "outputLabels": [
            "Bypass notification",
            "Check for notifications"
        ]
    },
    {
        "id": "974adda.d2ea92",
        "type": "Subflow",
        "name": "Set Volume Level",
        "info": "Set Volume Level",
        "category": "",
        "in": [
            {
                "x": 80,
                "y": 120,
                "wires": [
                    {
                        "id": "4c86fd47.0875e4"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 960,
                "y": 100,
                "wires": [
                    {
                        "id": "263d05a1.5b6672",
                        "port": 0
                    }
                ]
            }
        ],
        "status": {
            "x": 240,
            "y": 40,
            "wires": [
                {
                    "id": "7a662cd5.162a24",
                    "port": 0
                }
            ]
        }
    },
    {
        "id": "563c252e.ce31e4",
        "type": "function",
        "z": "974adda.d2ea92",
        "name": "Set Volume Level Payload",
        "func": "const entity = msg.payload && msg.payload.entity_id;\nconst attributes = msg.data && msg.data.attributes\nif (!entity || !attributes) {\n    node.status({ fill: \"red\", shape: \"dot\", text: \"Invalid volume payload\" });\n    return [null, null];\n}\n\nconst desired_volume_level = msg.payload.volume_level || 0;\nif (desired_volume_level < 0.0 || desired_volume_level > 1.0) {\n    let message = \"Volume level must be between 0 and 1.\"; \n    node.status({ fill: \"red\", shape: \"dot\", text: message });\n    node.error(message);\n    \n    return [null, { \n        payload: {\n            entity_id: entity,\n            message: message\n        }\n    }];\n}\n\nif (desired_volume_level === attributes.volume_level) {\n    let message = \"Volume level is already at the desired level.\"; \n    node.status({ fill: \"grey\", shape: \"dot\", text: message });\n    node.log(message);\n    return [null, { \n        payload: {\n            entity_id: entity,\n            message: message\n        }\n    }];\n}\n\nconst volumePayload = { \n    payload: {\n        data: {\n            entity_id: entity,\n            volume_level: desired_volume_level\n        } \n    }\n};\n\nlet message = \"Setting Volume Level to \" + (desired_volume_level * 100) + \" percent.\";\nconst speechPayload = { \n    payload: {\n        entity_id: entity,\n        message: message\n    }\n};\n\nnode.status({ fill: \"green\", shape: \"dot\", text: message });\nreturn [volumePayload, speechPayload];",
        "outputs": 2,
        "noerr": 0,
        "x": 510,
        "y": 120,
        "wires": [
            [
                "263d05a1.5b6672"
            ],
            [
                "b41308c0.f41e9"
            ]
        ],
        "inputLabels": [
            "Volume Percentage"
        ],
        "outputLabels": [
            "Volume Level Payload",
            "Speech Notification Payload"
        ]
    },
    {
        "id": "263d05a1.5b6672",
        "type": "api-call-service",
        "z": "974adda.d2ea92",
        "name": "Set Volume Level",
        "server": "61956bd4.93df44",
        "version": 1,
        "debugenabled": false,
        "service_domain": "media_player",
        "service": "volume_set",
        "entityId": "",
        "data": "",
        "dataType": "json",
        "mergecontext": "",
        "output_location": "payload",
        "output_location_type": "msg",
        "mustacheAltTags": false,
        "x": 770,
        "y": 100,
        "wires": [
            []
        ]
    },
    {
        "id": "b41308c0.f41e9",
        "type": "Subflow:7a62a2d3.c0f4a4",
        "z": "974adda.d2ea92",
        "name": "",
        "env": [],
        "x": 830,
        "y": 160,
        "wires": [
            []
        ]
    },
    {
        "id": "4c86fd47.0875e4",
        "type": "api-current-state",
        "z": "974adda.d2ea92",
        "name": "Get Current Volume Level",
        "server": "61956bd4.93df44",
        "version": 1,
        "outputs": 1,
        "halt_if": "",
        "halt_if_type": "str",
        "halt_if_compare": "is",
        "override_topic": true,
        "entity_id": "",
        "state_type": "str",
        "state_location": "",
        "override_payload": "none",
        "entity_location": "data",
        "override_data": "msg",
        "blockInputOverrides": false,
        "x": 250,
        "y": 120,
        "wires": [
            [
                "563c252e.ce31e4"
            ]
        ]
    },
    {
        "id": "7a662cd5.162a24",
        "type": "status",
        "z": "974adda.d2ea92",
        "name": "",
        "scope": null,
        "x": 120,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "76be143e.dbb0e4",
        "type": "Subflow",
        "name": "Set Volume Level on All Media Devices",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 100,
                "y": 120,
                "wires": [
                    {
                        "id": "642c5f33.bd92a8"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 1120,
                "y": 120,
                "wires": [
                    {
                        "id": "15d5ddae.8f64c2",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [],
        "color": "#DDAA99",
        "status": {
            "x": 1120,
            "y": 40,
            "wires": [
                {
                    "id": "ba88f8d5.b5d648",
                    "port": 0
                },
                {
                    "id": "15d5ddae.8f64c2",
                    "port": 0
                }
            ]
        }
    },
    {
        "id": "f467a711.24ca3",
        "type": "ha-get-entities",
        "z": "76be143e.dbb0e4",
        "server": "61956bd4.93df44",
        "name": "Get All Media Players",
        "rules": [
            {
                "property": "attributes.supported_features",
                "logic": "is",
                "value": "56253",
                "valueType": "num"
            },
            {
                "property": "attributes.available",
                "logic": "is",
                "value": "true",
                "valueType": "bool"
            }
        ],
        "output_type": "split",
        "output_empty_results": true,
        "output_location_type": "msg",
        "output_location": "payload",
        "output_results_count": 1,
        "x": 480,
        "y": 120,
        "wires": [
            [
                "1fe46c89.0e0543"
            ]
        ]
    },
    {
        "id": "1fe46c89.0e0543",
        "type": "change",
        "z": "76be143e.dbb0e4",
        "name": "Set Volume Level Payload",
        "rules": [
            {
                "t": "set",
                "p": "payload.volume_level",
                "pt": "msg",
                "to": "volume_level",
                "tot": "flow"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 730,
        "y": 120,
        "wires": [
            [
                "15d5ddae.8f64c2"
            ]
        ]
    },
    {
        "id": "642c5f33.bd92a8",
        "type": "function",
        "z": "76be143e.dbb0e4",
        "name": "Parse Volume Level",
        "func": "const volumeLevel = parseFloat(msg.payload);\nnode.status({ fill: \"green\", shape: \"dot\", text: \"Volume Level: \" + volumeLevel });\nflow.set(\"volume_level\", volumeLevel);\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 250,
        "y": 120,
        "wires": [
            [
                "f467a711.24ca3"
            ]
        ]
    },
    {
        "id": "15d5ddae.8f64c2",
        "type": "Subflow:974adda.d2ea92",
        "z": "76be143e.dbb0e4",
        "name": "",
        "env": [],
        "x": 970,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "ba88f8d5.b5d648",
        "type": "status",
        "z": "76be143e.dbb0e4",
        "name": "",
        "scope": null,
        "x": 140,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "3c07a459.ce903c",
        "type": "tab",
        "label": "Normalize Volume",
        "disabled": false,
        "info": "Normalize All Volume"
    },
    {
        "id": "e949c86f.c9ca9",
        "type": "api-current-state",
        "z": "3c07a459.ce903c",
        "name": "Automations Enabled?",
        "server": "61956bd4.93df44",
        "version": 1,
        "outputs": 2,
        "halt_if": "true",
        "halt_if_type": "bool",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "input_boolean.automation_enable",
        "state_type": "habool",
        "state_location": "",
        "override_payload": "none",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 560,
        "y": 100,
        "wires": [
            [
                "ec5308f5.aefff"
            ],
            []
        ],
        "outputLabels": [
            "",
            "enabled"
        ]
    },
    {
        "id": "ec5308f5.aefff",
        "type": "api-current-state",
        "z": "3c07a459.ce903c",
        "name": "Normalize Volume Levels?",
        "server": "61956bd4.93df44",
        "version": 1,
        "outputs": 2,
        "halt_if": "true",
        "halt_if_type": "bool",
        "halt_if_compare": "is",
        "override_topic": false,
        "entity_id": "input_boolean.automation_normalize_volume",
        "state_type": "habool",
        "state_location": "",
        "override_payload": "none",
        "entity_location": "",
        "override_data": "none",
        "blockInputOverrides": false,
        "x": 810,
        "y": 100,
        "wires": [
            [
                "bde48ad8.814ac"
            ],
            []
        ],
        "outputLabels": [
            "",
            "enabled"
        ]
    },
    {
        "id": "dbf47096.3a7dc",
        "type": "inject",
        "z": "3c07a459.ce903c",
        "name": "",
        "topic": "",
        "payload": "on",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 110,
        "y": 80,
        "wires": [
            [
                "4608374e.2fd4d8"
            ]
        ]
    },
    {
        "id": "6eace9df.a5aa68",
        "type": "inject",
        "z": "3c07a459.ce903c",
        "name": "",
        "topic": "",
        "payload": "off",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 110,
        "y": 120,
        "wires": [
            [
                "4608374e.2fd4d8"
            ]
        ]
    },
    {
        "id": "4608374e.2fd4d8",
        "type": "bigtimer",
        "z": "3c07a459.ce903c",
        "outtopic": "",
        "outpayload1": ".3",
        "outpayload2": ".1",
        "name": "Normalize Volume",
        "comment": "Set volume to 30% from 9:00am to 9:00pm, otherwise set to 10%",
        "lat": "44.50",
        "lon": "-88.06",
        "starttime": "540",
        "endtime": "1260",
        "startoff": "0",
        "endoff": 0,
        "startoff2": "",
        "endoff2": "",
        "offs": 0,
        "outtext1": "",
        "outtext2": "",
        "timeout": "240",
        "sun": true,
        "mon": true,
        "tue": true,
        "wed": true,
        "thu": true,
        "fri": true,
        "sat": true,
        "jan": true,
        "feb": true,
        "mar": true,
        "apr": true,
        "may": true,
        "jun": true,
        "jul": true,
        "aug": true,
        "sep": true,
        "oct": true,
        "nov": true,
        "dec": true,
        "day1": 0,
        "month1": 0,
        "day2": 0,
        "month2": 0,
        "day3": 0,
        "month3": 0,
        "day4": 0,
        "month4": 0,
        "day5": 0,
        "month5": 0,
        "day6": 0,
        "month6": 0,
        "day7": "",
        "month7": "",
        "day8": "",
        "month8": "",
        "day9": "",
        "month9": "",
        "day10": "",
        "month10": "",
        "day11": "",
        "month11": "",
        "day12": "",
        "month12": "",
        "d1": 0,
        "w1": 0,
        "d2": 0,
        "w2": 0,
        "d3": 0,
        "w3": 0,
        "d4": 0,
        "w4": 0,
        "d5": 0,
        "w5": 0,
        "d6": 0,
        "w6": 0,
        "xday1": "0",
        "xmonth1": "0",
        "xday2": "0",
        "xmonth2": "0",
        "xday3": 0,
        "xmonth3": 0,
        "xday4": 0,
        "xmonth4": 0,
        "xday5": 0,
        "xmonth5": 0,
        "xday6": 0,
        "xmonth6": 0,
        "xd1": 0,
        "xw1": 0,
        "xd2": 0,
        "xw2": 0,
        "xd3": 0,
        "xw3": 0,
        "xd4": 0,
        "xw4": 0,
        "xd5": 0,
        "xw5": 0,
        "xd6": 0,
        "xw6": 0,
        "suspend": false,
        "random": false,
        "repeat": false,
        "atstart": false,
        "odd": false,
        "even": false,
        "x": 340,
        "y": 100,
        "wires": [
            [
                "e949c86f.c9ca9"
            ],
            [],
            []
        ]
    },
    {
        "id": "418865ec.975eec",
        "type": "inject",
        "z": "3c07a459.ce903c",
        "name": "",
        "topic": "",
        "payload": "auto",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 110,
        "y": 160,
        "wires": [
            [
                "4608374e.2fd4d8"
            ]
        ]
    },
    {
        "id": "e314dd72.39afe",
        "type": "inject",
        "z": "3c07a459.ce903c",
        "name": "",
        "topic": "",
        "payload": "manual",
        "payloadType": "str",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 110,
        "y": 200,
        "wires": [
            [
                "4608374e.2fd4d8"
            ]
        ]
    },
    {
        "id": "bde48ad8.814ac",
        "type": "rbe",
        "z": "3c07a459.ce903c",
        "name": "Only allow changed values",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "property": "payload",
        "x": 480,
        "y": 180,
        "wires": [
            [
                "4173c48c.381f84"
            ]
        ]
    },
    {
        "id": "4173c48c.381f84",
        "type": "Subflow:76be143e.dbb0e4",
        "z": "3c07a459.ce903c",
        "name": "",
        "env": [],
        "x": 790,
        "y": 180,
        "wires": [
            []
        ]
    },
    {
        "id": "d45759b3.b76fe8",
        "type": "comment",
        "z": "3c07a459.ce903c",
        "name": "Set volume to 30% from 9:00am to 9:00pm, otherwise set to 10%",
        "info": "",
        "x": 270,
        "y": 40,
        "wires": []
    },
    {
        "id": "61956bd4.93df44",
        "type": "server",
        "z": "",
        "name": "Home Assistant",
        "legacy": false,
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": true
    }
]

NodeRed VS Home Assistant Automations

I personally prefer writing all my automations in NodeRed as it helps me visually see the flow of the automation. I’d love to hear what you like and if you’d like to see automations written using the built in Home Assistant Automations.

Join the mailing list

Get notified of new posts and related content to your inbox. I will never sell you anything and I will NEVER sell your email address.