Behringer xAir to Home Assistant

Adds Behringer xAir mixer support to Home Assistant.

[{"id":"5e89dcaa06413f09","type":"subflow","name":"HASS Event","info":"","category":"","in":[],"out":[{"x":860,"y":140,"wires":[{"id":"7c798a8f61be78ab","port":0}]}],"env":[{"name":"Event","type":"str","value":"","ui":{"type":"select","opts":{"opts":[{"l":{"en-US":"Loaded"},"v":"loaded"},{"l":{"en-US":"Script Update"},"v":"scriptUpdate"}]}}}],"meta":{},"color":"#DDAA99"},{"id":"2306a1c4ae9bd0d7","type":"function","z":"5e89dcaa06413f09","name":"function 18","func":"switch (env.get(\"Event\")) {\n    case \"loaded\": {\n        if (msg.topic == \"home_assistant_client:ready\" && msg.payload == \"ready\") {\n            msg.delay = 1;\n            return msg;\n        }\n        break;\n    }\n    case \"scriptUpdate\": {\n        if (msg.topic == \"call_service\" && msg.payload.event.domain == \"script\" && msg.payload.event.service == \"reload\") {\n            return msg;\n        }\n        break;\n    }\n    case \"disconnected\": {\n        if (msg.topic == \"home_assistant_client\" && msg.payload == \"disconnected\") {\n            return msg;\n        }\n        break;\n    }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":140,"wires":[["7c798a8f61be78ab"]]},{"id":"0bbb1a7f6689f423","type":"server-events","z":"5e89dcaa06413f09","name":"","server":"","version":1,"event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":320,"y":140,"wires":[["2306a1c4ae9bd0d7"]]},{"id":"7c798a8f61be78ab","type":"delay","z":"5e89dcaa06413f09","name":"","pauseType":"delayv","timeout":"0","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":720,"y":140,"wires":[[]]},{"id":"4efbf8f6c335d350","type":"server-events","z":"5e89dcaa06413f09","name":"","server":"","version":1,"event_type":"call_service","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":350,"y":180,"wires":[["2306a1c4ae9bd0d7"]]},{"id":"67420b25cac30abb","type":"comment","z":"a49785d74e501672","name":"xAir","info":"","x":250,"y":640,"wires":[]},{"id":"a4b3a5cc25c0eb2b","type":"udp in","z":"a49785d74e501672","name":"xAir In","iface":"","port":"10024","ipv":"udp4","multicast":"false","group":"","datatype":"buffer","x":250,"y":900,"wires":[["511081e05994a54b"]]},{"id":"01098a3b97a033f4","type":"osc","z":"a49785d74e501672","name":"","path":"","metadata":false,"x":570,"y":800,"wires":[["c3d4a51a8110c8f0"]]},{"id":"0fb519cb12f46c6f","type":"osc","z":"a49785d74e501672","name":"","path":"","metadata":false,"x":570,"y":900,"wires":[["1d968cfee2f6c56b"]]},{"id":"378577c0a6430da4","type":"function","z":"a49785d74e501672","name":"Subscribe to updates","func":"var msgs = [];\nfor(var i in flow.get(\"xAirDevices\")) {\n    msgs.push({\n        \"topic\": \"/xremote\",\n        \"ip\": flow.get(\"xAirDevices\")[i].ip\n    });\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":820,"wires":[["01098a3b97a033f4"]]},{"id":"511081e05994a54b","type":"function","z":"a49785d74e501672","name":"Get device","func":"var devices = flow.get(\"xAirDevices\");\nif(!devices){return;}\n\nfor(var i in devices) {\n    if(msg.ip == devices[i].ip) {\n        msg.deviceId = i;\n        msg.device = devices[i];\n        return msg;\n    }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":900,"wires":[["0fb519cb12f46c6f"]]},{"id":"c3d4a51a8110c8f0","type":"udp out","z":"a49785d74e501672","name":"","addr":"","iface":"","port":"10024","ipv":"udp4","outport":"","base64":false,"multicast":"false","x":710,"y":800,"wires":[]},{"id":"373c118fbbe9b4fd","type":"inject","z":"a49785d74e501672","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":235,"y":820,"wires":[["378577c0a6430da4"]],"l":false},{"id":"5ce106d184d4147d","type":"function","z":"a49785d74e501672","name":"Get information","func":"var msgs = [];\nvar devices = flow.get(\"xAirDevices\");\nfor(var i in devices) {\n    //Channels\n    for(var j in devices[i].channels) {\n        var channels = devices[i].channels[j];\n        if(!Array.isArray(channels)){channels = [channels];}\n        \n        for(var k in channels) {\n            msgs.push({\n                \"topic\": `/ch/${('0' + channels[k]).slice(-2)}/mix/fader`,\n                \"ip\": devices[i].ip\n            });\n            msgs.push({\n                \"topic\": `/ch/${('0' + channels[k]).slice(-2)}/mix/on`,\n                \"ip\": devices[i].ip\n            });\n        }\n    }\n    \n    //Aux\n    msgs.push({\n        \"topic\": `/rtn/aux/mix/fader`,\n        \"ip\": devices[i].ip\n    });\n    msgs.push({\n        \"topic\": `/rtn/aux/mix/on`,\n        \"ip\": devices[i].ip\n    });\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":780,"wires":[["01098a3b97a033f4"]]},{"id":"d7ac3abf8801e8e8","type":"function","z":"a49785d74e501672","name":"Set xAir Devices","func":"flow.set(\"xAirDevices\", {\n    //Example device 1\n    \"Example\": {\n        \"ip\": \"192.168.0.1\",\n        \"channels\": {\n            \"Channel 1\": 9,\n            \"Channel 2\": [11, 12]\n        },\n        \"snapshots\": {\n            \"Snap\": 1\n        }\n    },\n    //Example device 2\n    \"Example 2\": {\n        \"ip\": \"192.168.0.1\",\n        \"channels\": {\n            \"Channel 1\": 9,\n            \"Channel 2\": [11, 12]\n        },\n        \"snapshots\": {\n            \"Snap\": 1\n        }\n    }\n});","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":680,"wires":[[]]},{"id":"5bf09da17c938a15","type":"inject","z":"a49785d74e501672","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":235,"y":680,"wires":[["d7ac3abf8801e8e8"]],"l":false},{"id":"1d968cfee2f6c56b","type":"function","z":"a49785d74e501672","name":"","func":"var devices = flow.get(\"xAirDevices\");\nif(!devices){return;}\n\nvar msgs = [];\nif(msg.device) {\n    var id = parseInt(msg.topic.split(\"/\")[2]);\n    var channel;\n    if(msg.topic.includes(\"/ch/\")) {\n        for(var i in msg.device.channels) {\n            if((Array.isArray(msg.device.channels[i]) && msg.device.channels[i].includes(id)) || (msg.device.channels[i] == id)) {\n                channel = i;\n            }\n        }\n    }\n    \n    if(channel && msg.topic.includes(\"/mix/fader\")) {\n        return {\n            \"entity_id\": `number.xair_${msg.deviceId.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_${channel.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_volume`,\n            \"payload\": {\n                \"data\": {\n                    \"state\": Math.round(msg.payload * 100).toString(),\n                    \"attributes\": {\n                        \"friendly_name\": `${msg.deviceId} ${channel} Volume`,\n                        \"device\": msg.deviceId,\n                        \"channel\": channel,\n                        \"id\": id,\n                        \"editable\": true,\n                        \"min\": 0,\n                        \"max\": 100,\n                        \"step\": 1,\n                        \"unit_of_measurement\": \"%\"\n                    }\n                }\n            }\n        }\n    }\n    else if(channel && msg.topic.includes(\"/mix/on\")) {\n        if(!devices[msg.deviceId][\"channelMutes\"]) { devices[msg.deviceId][\"channelMutes\"] = {};}\n        devices[msg.deviceId][\"channelMutes\"][channel] = msg.payload == 1;\n        return {\n            \"entity_id\": `input_boolean.xair_${msg.deviceId.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_${channel.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_mute`,\n            \"payload\": {\n                \"data\": {\n                    \"state\": msg.payload ? \"off\" : \"on\",\n                    \"attributes\": {\n                        \"friendly_name\": `${msg.deviceId} ${channel} Mute`,\n                        \"device\": msg.deviceId,\n                        \"channel\": channel,\n                        \"id\": id,\n                        \"editable\": true\n                    }\n                }\n            }\n        }\n    }\n}","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\nif(!flow.get(\"ampStates\")) {\n    flow.set(\"ampStates\", {});\n}","finalize":"","libs":[],"x":720,"y":900,"wires":[["527b0a5e0af508e1"]]},{"id":"527b0a5e0af508e1","type":"ha-api","z":"a49785d74e501672","name":"","server":"","version":1,"debugenabled":false,"protocol":"http","method":"post","path":"/api/states/{{entity_id}}","data":"","dataType":"json","responseType":"json","outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"results"}],"x":870,"y":900,"wires":[[]]},{"id":"52606663b51d8361","type":"server-events","z":"a49785d74e501672","name":"","server":"","version":1,"event_type":"call_service","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":310,"y":740,"wires":[["66e7b7eca3f7466f"]]},{"id":"66e7b7eca3f7466f","type":"function","z":"a49785d74e501672","name":"Action","func":"var devices = flow.get(\"xAirDevices\");\nif(!devices){return;}\n\nif(typeof msg.payload.event.service_data.entity_id == \"string\"){msg.payload.event.service_data.entity_id = [msg.payload.event.service_data.entity_id];}\nvar msgs = [];\nfor(var i in msg.payload.event.service_data.entity_id) {\n    var entity_id = msg.payload.event.service_data.entity_id[i];\n    if(entity_id.includes(\"xair_\")){\n        var entityIdSplits = entity_id.split(\".\")[1].split(\"_\");\n        \n        //Find the device\n        var device;\n        for(var j in devices) {\n            if (j.replace(/[^A-Za-z0-9]/g, '').toLowerCase() == entityIdSplits[1]) {\n                device = devices[j];\n            }\n        }\n        if(!device){return;}\n        \n        //Find the channel\n        var channels;\n        for(var j in device.channels) {\n            if (j.replace(/[^A-Za-z0-9]/g, '').toLowerCase() == entityIdSplits[2]) {\n                channels = device.channels[j];\n                if(!Array.isArray(channels)){channels = [channels];}\n            }\n        }\n        \n        switch(msg.payload.event.domain) {\n            //Volume\n            case \"number\": {\n                switch(msg.payload.event.service) {\n                    case \"set_value\": {\n                        for(var k in channels) {\n                            msgs.push({\n                                \"topic\": `/ch/${('0' + channels[k]).slice(-2)}/mix/fader`,\n                                \"payload\": parseInt(msg.payload.event.service_data.value) / 100,\n                                \"ip\": device.ip,\n                            });\n                        }\n                    }\n                }\n                break;\n            }\n            //Mute\n            case \"input_boolean\": {\n                var value;\n                switch(msg.payload.event.service) {\n                    case \"turn_on\": {\n                        value = 0;\n                        break;\n                    }\n                    case \"turn_off\": {\n                        value = 1;\n                        break;\n                    }\n                    case \"toggle\": {\n                        value = devices[entityIdSplits[1]][\"channelMutes\"][entityIdSplits[2]] ? 0 : 1\n                        break;\n                    }\n                }\n                for(var k in channels) {\n                    msgs.push({\n                        \"topic\": `/ch/${('0' + channels[k]).slice(-2)}/mix/on`,\n                        \"payload\": value,\n                        \"ip\": device.ip\n                    });\n                }\n                break;\n            }\n            case \"button\": {\n                if(msg.payload.event.service == \"press\") {\n                    if(entityIdSplits[2] != \"snapshot\"){return;}\n                    \n                    //Find the snapshot\n                    var snapshotId;\n                    for(var j in device.snapshots) {\n                        if (j.replace(/[^A-Za-z0-9]/g, '').toLowerCase() == entityIdSplits[3]) {\n                            snapshotId = device.snapshots[j];\n                        }\n                    }\n                    \n                    msgs.push({\n                        \"topic\": `/-snap/load`,\n                        \"payload\": snapshotId,\n                        \"ip\": device.ip\n                    });\n                }\n            }\n        }\n    }\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":740,"wires":[["01098a3b97a033f4"]]},{"id":"4df2505199f69f02","type":"function","z":"a49785d74e501672","name":"Add buttons","func":"var msgs = [];\nvar devices = flow.get(\"xAirDevices\");\nfor(var i in devices) {\n    //Snapshots\n    for(var j in devices[i].snapshots) {\n        msgs.push({\n            \"entity_id\": `button.xair_${i.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_snapshot_${j.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}`,\n            \"payload\": {\n                \"data\": {\n                    \"state\": \"unknown\",\n                    \"attributes\": {\n                        \"friendly_name\": `${i} Snapshot ${j}`,\n                        \"editable\": true\n                    }\n                }\n            }\n        });  \n    }\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":860,"wires":[["527b0a5e0af508e1"]]},{"id":"49e050de7382591b","type":"subflow:5e89dcaa06413f09","z":"a49785d74e501672","name":"Loaded","env":[{"name":"Event","value":"loaded","type":"str"}],"x":250,"y":780,"wires":[["5ce106d184d4147d"]]},{"id":"04b7974a3938b51c","type":"subflow:5e89dcaa06413f09","z":"a49785d74e501672","name":"Loaded","env":[{"name":"Event","value":"loaded","type":"str"}],"x":570,"y":860,"wires":[["4df2505199f69f02"]]}]

Flow Info

Created 3 years, 10 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • comment (x1)
  • delay (x1)
  • function (x8)
  • inject (x2)
  • udp in (x1)
  • udp out (x1)
Other
  • ha-api (x1)
  • osc (x2)
  • server-events (x3)
  • subflow (x1)
  • subflow:5e89dcaa06413f09 (x2)

Tags

  • behringer
  • xair
  • xair12
  • xair18
  • homeassistant
  • home
  • assistant
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option