How-to normalize home volume levels with Node-RED

栏目: IT技术 · 发布时间: 4年前

内容简介:This post is going to cover normalizing allThis automation normalizes all volume levels in my home based on the time of day using theThis is one of my favorite automations in my house and I think you will enjoy it too! I wrote this automation for several r

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 multipleInput Booleansthat 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_boolean s 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.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C算法(第二卷:图算法)(第3版)

C算法(第二卷:图算法)(第3版)

塞德威克(Sedgewick Robert) / 周良忠 / 第1版 (2004年1月1日) / 2004-4 / 38.0

《C算法(第2卷)(图算法)(第3版)(中文版)》所讨论的图算法,都是实际中解决图问题的最重要的已知方法。《C算法(第2卷)(图算法)(第3版)(中文版)》的主要宗旨是让越来越多需要了解这些算法的人的能够掌握这些方法及基本原理。书中根据基本原理从基本住处开始循序渐进地讲解,然后再介绍一些经典方法,最后介绍仍在进行研究和发展的现代技术。精心挑选的实例、详尽的图示以及完整的实现代码与正文中的算法和应用......一起来看看 《C算法(第二卷:图算法)(第3版)》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具