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"]]}]