Behringer x32 to Home Assistant
Adds the Behringer x32 mixer 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":"a139c1176fb91368","type":"comment","z":"a49785d74e501672","name":"x32","info":"","x":250,"y":700,"wires":[]},{"id":"78cc2d578fdb6fdf","type":"udp in","z":"a49785d74e501672","name":"x32In","iface":"","port":"10025","ipv":"udp4","multicast":"false","group":"","datatype":"buffer","x":250,"y":900,"wires":[["4c0c5d47dc8cb69a"]]},{"id":"6a69af6e90e25094","type":"osc","z":"a49785d74e501672","name":"","path":"","metadata":false,"x":550,"y":860,"wires":[["a0d73c3e891687af"]]},{"id":"f4cc69e670da42d0","type":"function","z":"a49785d74e501672","name":"Subscribe to updates","func":"var msgs = [];\nfor(var i in flow.get(\"x32Devices\")) {\n msgs.push({\n \"topic\": \"/xremote\",\n \"ip\": flow.get(\"x32Devices\")[i].ip\n });\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":860,"wires":[["6a69af6e90e25094"]]},{"id":"a0d73c3e891687af","type":"udp out","z":"a49785d74e501672","name":"","addr":"","iface":"","port":"10023","ipv":"udp4","outport":"10025","base64":false,"multicast":"false","x":690,"y":860,"wires":[]},{"id":"b994656caae77cbf","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":860,"wires":[["f4cc69e670da42d0"]],"l":false},{"id":"31eba5fd1fd772d8","type":"function","z":"a49785d74e501672","name":"Get information","func":"var msgs = [];\nvar devices = flow.get(\"x32Devices\");\nfor(var i in devices) {\n //Channels\n for(var j in devices[i].channels) {\n var channel = devices[i].channels[j];\n \n //If there are multiple channels set id to be the first channel\n if(Array.isArray(channel)) {\n channel = channel[0];\n }\n \n //Master\n msgs.push({\n \"topic\": `/ch/${('0' + channel).slice(-2)}/mix/fader`,\n \"ip\": devices[i].ip\n });\n msgs.push({\n \"topic\": `/ch/${('0' + channel).slice(-2)}/mix/on`,\n \"ip\": devices[i].ip\n });\n \n //Buses\n for(var k in devices[i].buses) {\n var bus = devices[i].buses[k];\n \n msgs.push({\n \"topic\": `/ch/${('0' + channel).slice(-2)}/mix/${('0' + bus).slice(-2)}/level`,\n \"ip\": devices[i].ip\n });\n msgs.push({\n \"topic\": `/ch/${('0' + channel).slice(-2)}/mix/${('0' + bus).slice(-2)}/on`,\n \"ip\": devices[i].ip\n });\n }\n }\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":820,"wires":[["6a69af6e90e25094"]]},{"id":"755012ef4d6274b5","type":"function","z":"a49785d74e501672","name":"Set x32 Devices","func":"flow.set(\"x32Devices\", {\n //Example device 1\n \"Example 1\": {\n \"ip\": \"192.16.0.1\",\n \"buses\": {\n \"Bus 1\": 1,\n \"Bus 2\": 2\n },\n \"channels\": {\n \"Channel 1\": 1,\n \"Channel 2\": [25, 26]\n }\n },\n //Example device 2\n \"Example 2\": {\n \"ip\": \"192.16.0.1\",\n \"buses\": {\n \"Bus 1\": 1,\n \"Bus 2\": 2\n },\n \"channels\": {\n \"Channel 1\": 1,\n \"Channel 2\": [25, 26]\n }\n }\n});\n\n///////////////////////////////////////////////////////\n///////////////////////////////////////////////////////\n\n//Setup our stores for information\nfor(var i in flow.get(\"x32Devices\")) {\n flow.get(\"x32Devices\")[i][\"busValues\"] = {\n \"master\": {\n \"levels\": {},\n \"mutes\": {}\n }\n }\n \n for(var j in flow.get(\"x32Devices\")[i].buses) {\n flow.get(\"x32Devices\")[i][\"busValues\"][j] = {\n \"levels\": {},\n \"mutes\": {}\n }\n }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":740,"wires":[[]]},{"id":"77288f011112c51c","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":740,"wires":[["755012ef4d6274b5"]],"l":false},{"id":"043d2f8d72425c8c","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":290,"y":780,"wires":[["652d666e21c1fb01"]]},{"id":"d7786aa24bf0c753","type":"function","z":"a49785d74e501672","name":"Action","func":"var entity_id = msg.entity_id || msg.data.entity_id;\nvar entityIdSplits = entity_id.split(\".\")[1].split(\"_\");\nvar devices = flow.get(\"x32Devices\");\nif(!devices){return;}\nvar service = msg.event.service;\nvar serviceData = msg.event.service_data;\n\nvar device;\nvar bus;\nvar ids;\n\n//Find the device\nfor(var i in devices) {\n if (i.replace(/[^A-Za-z0-9]/g, '').toLowerCase() == entityIdSplits[1]) {\n device = devices[i];\n break;\n }\n}\n\n//Find the bus\nfor (var i in device.buses) {\n if (i.replace(/[^A-Za-z0-9]/g, '').toLowerCase() == entityIdSplits[2]) {\n bus = device.buses[i];\n break;\n }\n}\n\n//Find the channel\nfor (var i in device.channels) {\n if (i.replace(/[^A-Za-z0-9]/g, '').toLowerCase() == entityIdSplits[3]) {\n ids = device.channels[i];\n break;\n }\n}\n\nif (!Array.isArray(ids)) { ids = [ids]; }\nif(!device || !bus || !ids){return;}\n\nvar msgs = [];\nswitch (msg.event.domain) {\n //Volume\n case \"number\": {\n switch (service) {\n case \"set_value\": {\n if (bus == \"master\") {\n for (var i in ids) {\n var value = parseInt(serviceData.value) / 100;\n msg.push({\n \"topic\": `/ch/${('0' + ids[i]).slice(-2)}/mix/fader`,\n \"payload\": value,\n \"ip\": device.ip,\n });\n }\n }\n else {\n for (var i in ids) {\n var value = parseInt(serviceData.value) / 100;\n msgs.push({\n \"topic\": `/ch/${('0' + ids[i]).slice(-2)}/mix/${('0' + bus).slice(-2)}/level`,\n \"payload\": value,\n \"ip\": device.ip,\n });\n }\n }\n break;\n }\n }\n break;\n }\n //Mute\n case \"input_boolean\": {\n var value = 0;\n switch (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 = device[\"busValues\"][bus].mutes[ids[i]] ? 0 : 1\n break;\n }\n }\n\n if (bus == \"master\") {\n for (var i in ids) {\n msgs.push({\n \"topic\": `/ch/${('0' + ids[i]).slice(-2)}/mix/on`,\n \"payload\": value,\n \"ip\": device.ip\n });\n }\n }\n else {\n for (var i in ids) {\n msgs.push({\n \"topic\": `/ch/${('0' + ids[i]).slice(-2)}/mix/${('0' + bus).slice(-2)}/on`,\n \"payload\": value,\n \"ip\": device.ip\n });\n }\n }\n break;\n }\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":780,"wires":[["ee3df45e42b854ea"]]},{"id":"90553d545ba4742f","type":"osc","z":"a49785d74e501672","name":"","path":"","metadata":false,"x":550,"y":900,"wires":[["f701fcd927c877ac"]]},{"id":"f701fcd927c877ac","type":"function","z":"a49785d74e501672","name":"Create entity","func":"var devices = flow.get(\"x32Devices\");\nvar msgs = [];\n\n//Find the channel\nfunction findChannel(device, id) {\n for (var i in device.channels) {\n var check = device.channels[i];\n if (!Array.isArray(check)) { check = [check]; }\n\n for (var j in check) {\n if (check[j] == id) {\n return i;\n }\n }\n }\n}\n\n//Find the bus\nfunction findBus(device, id) {\n for (var i in device.buses) {\n var check = device.buses[i];\n if (!Array.isArray(check)) { check = [check]; }\n\n for (var j in check) {\n if (check[j] == id) {\n return i;\n }\n }\n }\n}\n\nif (msg.device) {\n if (msg.topic.includes(\"/ch/\") && msg.topic.includes(\"/mix/fader\")) {\n var id = findChannel(msg.device, parseInt(msg.topic.split(\"/\")[2]));\n\n if (id) {\n flow.get(\"x32Devices\")[msg.deviceId][\"busValues\"][\"master\"].levels[id] = msg.payload;\n return {\n \"entity_id\": `number.x32_${msg.deviceId.toLowerCase()}_master_${id.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_volume`,\n \"payload\": {\n \"data\": {\n \"state\": Math.ceil(msg.payload * 100).toString(),\n \"attributes\": {\n \"friendly_name\": `${id} Volume`,\n \"device\": msg.deviceId,\n \"bus\": \"master\",\n \"id\": id,\n \"editable\": true,\n \"min\": 0,\n \"max\": 100,\n \"step\": 1,\n \"unit_of_measurement\": \"%\"\n }\n }\n },\n \"channels\": flow.get(\"x32Devices\")[msg.deviceId][\"busValues\"]\n }\n }\n }\n else if (msg.topic.includes(\"/ch/\") && msg.topic.includes(\"/mix/on\")) {\n var id = findChannel(msg.device, parseInt(msg.topic.split(\"/\")[2]));\n\n if (id) {\n flow.get(\"x32Devices\")[msg.deviceId][\"busValues\"][\"master\"].mutes[id] = msg.payload;\n return {\n \"entity_id\": `input_boolean.x32_${msg.deviceId.toLowerCase()}_master_${id.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_mute`,\n \"payload\": {\n \"data\": {\n \"state\": msg.payload ? \"off\" : \"on\",\n \"attributes\": {\n \"friendly_name\": `${id} Mute`,\n \"bus\": \"master\",\n \"id\": id,\n \"device\": msg.deviceId,\n \"editable\": true\n }\n }\n }\n }\n }\n }\n else if (msg.topic.includes(\"/ch/\") && msg.topic.includes(\"/mix/\") && msg.topic.includes(\"/level\")) {\n var id = findChannel(msg.device, parseInt(msg.topic.split(\"/\")[2]));\n var bus = findBus(msg.device, parseInt(msg.topic.split(\"/\")[4]));\n\n if (id && bus) {\n flow.get(\"x32Devices\")[msg.deviceId][\"busValues\"][bus].levels[id] = msg.payload;\n return {\n \"entity_id\": `number.x32_${msg.deviceId.toLowerCase()}_${bus.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_${id.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_volume`,\n \"payload\": {\n \"data\": {\n \"state\": Math.ceil(msg.payload * 100).toString(),\n \"attributes\": {\n \"friendly_name\": `${bus} ${id} Volume`,\n \"device\": msg.deviceId,\n \"bus\": bus,\n \"id\": id,\n \"editable\": true,\n \"min\": 0,\n \"max\": 100,\n \"step\": 1,\n \"unit_of_measurement\": \"%\"\n }\n }\n }, \n \"channels\": flow.get(\"x32Devices\")[msg.deviceId][\"busValues\"]\n }\n }\n }\n else if (msg.topic.includes(\"/ch/\") && msg.topic.includes(\"/mix/\") && msg.topic.includes(\"/on\")) {\n var id = findChannel(msg.device, parseInt(msg.topic.split(\"/\")[2]));\n var bus = findBus(msg.device, parseInt(msg.topic.split(\"/\")[4]));\n\n if (id && bus) {\n flow.get(\"x32Devices\")[msg.deviceId][\"busValues\"][bus].mutes[id] = msg.payload;\n return {\n \"entity_id\": `input_boolean.x32_${msg.deviceId.toLowerCase()}_${bus.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_${id.replace(/[^A-Za-z0-9]/g, '').toLowerCase()}_mute`,\n \"payload\": {\n \"data\": {\n \"state\": msg.payload ? \"off\" : \"on\",\n \"attributes\": {\n \"friendly_name\": `${bus} ${id} Mute`,\n \"bus\": bus,\n \"id\": id,\n \"device\": msg.deviceId,\n \"editable\": true\n }\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":690,"y":900,"wires":[["1756accb1dac8ea0"]]},{"id":"4c0c5d47dc8cb69a","type":"function","z":"a49785d74e501672","name":"Get device","func":"var devices = flow.get(\"x32Devices\");\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":[["90553d545ba4742f"]]},{"id":"1756accb1dac8ea0","type":"trigger","z":"a49785d74e501672","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"100","extend":true,"overrideDelay":false,"units":"ms","reset":"","bytopic":"topic","topic":"entity_id","outputs":1,"x":860,"y":900,"wires":[["ed9657ad1e983a10"]]},{"id":"ed9657ad1e983a10","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":1010,"y":900,"wires":[[]]},{"id":"85a519946809d4bb","type":"trigger","z":"a49785d74e501672","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"1","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":900,"y":780,"wires":[["31eba5fd1fd772d8"]]},{"id":"ee3df45e42b854ea","type":"osc","z":"a49785d74e501672","name":"","path":"","metadata":false,"x":750,"y":780,"wires":[["a0d73c3e891687af","4c0c5d47dc8cb69a","85a519946809d4bb"]]},{"id":"652d666e21c1fb01","type":"function","z":"a49785d74e501672","name":"Prepare","func":"if (typeof msg.payload.event.service_data.entity_id == \"string\") {\n msg.payload.event.service_data.entity_id = [msg.payload.event.service_data.entity_id];\n}\n\nvar msgs = [];\nfor (var i in msg.payload.event.service_data.entity_id) {\n if (msg.payload.event.service_data.entity_id[i].includes(\"x32_\")) {\n msgs.push({\n \"topic\": msg.topic,\n \"entity_id\": msg.payload.event.service_data.entity_id[i],\n \"event\": msg.payload.event,\n \"data\": msg.payload.event.service_data\n });\n }\n}\nreturn [msgs];","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":780,"wires":[["d7786aa24bf0c753"]]},{"id":"28b657a52c29f2a9","type":"subflow:5e89dcaa06413f09","z":"a49785d74e501672","name":"Loaded","env":[{"name":"Event","value":"loaded","type":"str"}],"x":250,"y":820,"wires":[["31eba5fd1fd772d8"]]}]