Stream decks are all the rage and a wearable stream deck design was an inevitable. Why wearable though, what does it give you over a regular stream deck when you are sitting at your PC streaming? Not much, that's why this stream deck and data pipeline is to control your Open Broadcasting Software remotely, across the globe. This is an IRL stream deck!
I am still in the process of building out node-red workflows, but the sky is the limit for controlling or triggering effects and commands based on anything that speaks internet. Websockets, http, udp, tcp, socket.io. If node.js can speak with it, so can this stream deck.
Tech demo glove controlling live stream effects during the IRL stream
Woo demos of vfx control using the glove (mqtt) to node-red so Magic Music Visuals via OSC out of node-red.
https://www.twitch.tv/atltvhead/clip/InnocentFreezingEyeballRalpherZ-s1QMuyXip6j_TF7K
https://clips.twitch.tv/CheerfulInventivePidgeonPraiseIt-iaRoMfRgHXwslbsG
some more demo!
https://clips.twitch.tv/PluckyPiliableBasenjiImGlitch-BFIjQvBhsSrbF_Tk
Build instructionsStep 1
FOUND IN THE GITHUB LINK IS A LINK TO BOTH THE FUSION360 FILE AND A STEP FILE VERSION OF THIS GLOVE/BRACER.
USING FUSION360 YOU CAN MODIFY THE ARM MODEL TO MATCH YOUR OWN. THIS WILL ALLOW THE GLOVE TO FIT YOU PROPERLY.
Step 2
This is not going to be an in-depth docker tutorial, but just the bare minimum. Search for
Docker, install for your system.
Then docker pull the following images: https://hub.docker.com/_/eclipse-mosquitto and https://hub.docker.com/r/nodered/node-redfollow those pages to run the containers.
Alternatively, you can install node-red on your system natively, since it may give you a little more flexibility with other software. You can also install eclipse mqtt on a raspberry pi, which is also popular to do. <igure>
Step 3
If you have not already, install OBS from their website.
Then download and install obs websockets. It is a fantastic plugin! It allows other scripts, programs, and containers to communicate and control obs via websockets!
Step 4
Node-red is based on Node.js, which is a javascript runtime environment.
Its really powerful on its own, but can use a little help to make it easier to communicate with obs. That comes in the form the node-red-contrib-obs-ws library, written by Leb! (Leb is a cool dude!) He detailed the skinny of how to install the library, but in docker it's a little different. While the node red container is running open another cmd or terminal and docker exec -it <node red container name> /bin/sh
. That'll get you into the container's shell environment.
From there you can follow Leb's installation instructions. Restart the container and you should see the following new nodes in Node-red.
Step 5
Electrical Connections
I used an ESP32 Thing Plus for the amount of button interrupts it offers and the qwiic connector.I connected the the gateron switches the esp32 pins to ground. The pins I used are 5, 12, 13, 14, 15, 1, 17, 18, 19, 21, 25, 26, 27, 32, and 33. I then connected an Adafruit LSM6DS33 + LIS3MDL - 9 DoF IMU to the Thing Plus with the qwiic connector.
I used this amazon power regulator and battery charger, running power and ground through the body of the glove and into the electrical carriage. Eventually connecting directly to the esp32.
Sorry about the lack of photos, I only had a few hours to actually assemble everything and didn't document very well.
CADI designed the electrical housing to be a little modular. I say a little because power will eventually tether it to the glove, but while assembly happens it can remain separate. A hole feature will allow for programming of the esp32 after installation. Snap features on the carriage will lock it into place within the glove housing.
I designed the a plate that allows for the gateron switches to snap into place, screw into the four posts in the electrical housing.
The keys are by far my favorite part. While I stream, IRL, I wanted some indicator of what button I am on before I pressed it so I did some index design.
The top row are just bumps going up in number and changing shape, the middle bars at different angles with one x, and the last row is standard Braille.
The electrical housing snaps into the bottom half of the glove. You'll see I also have some features in the left hand side of the photo. They are heat inserts for something I'll show off later.
Showing off some hinge feature so I can get in and out of the glove and a support structure. This support feature will help hook my camera/selfie stick during the live streams. Nothing worse than getting a shaky camera arm.
Gotta include a cutout for them wrist bones.
And a clip that holds the two halves of the glove together.
The top half of the glove has the power regulator board and a cover with hinge and snap features.
Lastly, the display! Since we are leveraging Node-red I might as well just make a dashboard page within that and output all my info there! What better to access a webpage than a smartphone! I used a pixel2, but will eventually make a version using 2 of this display and MQTT.
Step 6
It's in the GITHUB!
The code comes down the these smaller steps:
MQTT handling and Button handling.
Let's do button handling first.
I made a simple button object with the pin number, button press boolean, and button count.
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
};
Button button1 = {13, 0, false};
Button button2 = {12, 0, false};
Button button3 = {27, 0, false};
Button button4 = {33, 0, false};
Button button5 = {15, 0, false};
Button button6 = {32, 0, false};
Button button7 = {14, 0, false};
Button button8 = {26, 0, false};
Button button9 = {25, 0, false};
Button button10 = {21, 0, false};
Button button11 = {17, 0, false};
Button button12 = {16, 0, false};
Button button13 = {19, 0, false};
Button button14 = {18, 0, false};
Button button15 = {5, 0, false};
int button_debounce = 250;
int button_double_delay = 300;
Then we create an interrupt function... do this for every button... or whatever. There's probably a better way.
void IRAM_ATTR isr() {
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 350ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > button_debounce)
{
button1.numberKeyPresses += 1;
button1.pressed = true;
}
last_interrupt_time = interrupt_time;
}
In the setup we attach our button to your interrupt function
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);
Finally If any or multiple buttons have been pressed, create a json string of the current state of all buttons and send it out over mqtt
void button_handler(){
Binterrupt_time = millis();
if(Binterrupt_time - Blast_interrupt_time > button_double_delay){
if (button1.pressed || button2.pressed || button3.pressed || button4.pressed || button5.pressed || button6.pressed || button7.pressed || button8.pressed || button9.pressed || button10.pressed || button11.pressed || button12.pressed || button13.pressed || button14.pressed || button15.pressed) {
//doc["b1B"]=button1.pressed;
doc["b1"]=button1.numberKeyPresses;
//doc["b2B"]=button12.pressed;
doc["b2"]=button2.numberKeyPresses;
//doc["b3B"]=button3.pressed;
doc["b3"]=button3.numberKeyPresses;
//doc["b4B"]=button4.pressed;
doc["b4"]=button4.numberKeyPresses;
//doc["b5B"]=button5.pressed;
doc["b5"]=button5.numberKeyPresses;
//doc["b6B"]=button6.pressed;
doc["b6"]=button6.numberKeyPresses;
//doc["b7B"]=button7.pressed;
doc["b7"]=button7.numberKeyPresses;
//doc["b8B"]=button8.pressed;
doc["b8"]=button8.numberKeyPresses;
//doc["b9B"]=button9.pressed;
doc["b9"]=button9.numberKeyPresses;
//doc["b10B"]=button10.pressed;
doc["b10"]=button10.numberKeyPresses;
//doc["b11B"]=button11.pressed;
doc["b11"]=button11.numberKeyPresses;
//doc["b12B"]=button12.pressed;
doc["b12"]=button12.numberKeyPresses;
//doc["b13B"]=button13.pressed;
doc["b13"]=button13.numberKeyPresses;
//doc["b14B"]=button14.pressed;
doc["b14"]=button14.numberKeyPresses;
//doc["b15B"]=button15.pressed;
doc["b15"]=button15.numberKeyPresses;
char buf[256];
size_t n = serializeJson(doc, buf);
client.publish("button_glove", buf, n);
//Serial.println(n);
button1.pressed = false;
button2.pressed = false;
button3.pressed = false;
button4.pressed = false;
button5.pressed = false;
button6.pressed = false;
button7.pressed = false;
button8.pressed = false;
button9.pressed = false;
button10.pressed = false;
button11.pressed = false;
button12.pressed = false;
button13.pressed = false;
button14.pressed = false;
button15.pressed = false;
}
Blast_interrupt_time = Binterrupt_time;
}
}
For the mqtt, Include the necessary libraries and bit of structure for the json string
#include "EspMQTTClient.h"
#include <Arduino.h>
#include <ArduinoJson.h>
const int capacity = JSON_OBJECT_SIZE(15);
StaticJsonDocument<capacity> doc;
Now create an object for our mqtt passwords and addresses
EspMQTTClient client(
SSID2,
PASSWORD2,
MQTTBROKER, // MQTT Broker server ip
"TestESP32", // Client name that uniquely identify your device
1883 // The MQTT port, default to 1883. this line can be omitted
);
in the setup function include
// Optionnal functionnalities of EspMQTTClient :
client.enableDebuggingMessages(); // Enable debugging messages sent to serial output
client.enableHTTPWebUpdater(); // Enable the web updater. User and password default to values of MQTTUsername and MQTTPassword. These can be overrited with enableHTTPWebUpdater("user", "password").
client.enableLastWillMessage("TestClient/lastwill", "I am going offline"); // You can activate the retain flag by setting the third parameter to true
client.setMaxPacketSize(256);
we need a function to actually handle subbing to different topics we we want to, but it is needed to maintain connection to the mqtt server.
This function is called once everything is connected (Wifi and MQTT)
// WARNING : YOU MUST IMPLEMENT IT IF YOU USE EspMQTTClient
void onConnectionEstablished()
{
// Subscribe to "mytopic/test" and display received message to Serial
client.subscribe("stream_status", [](const String & payload) {
Serial.println(payload);
});
// Subscribe to "mytopic/test" and display received message to Serial
client.subscribe("obs_webS_Connection", [](const String & payload) {
Serial.println(payload);
});
// Subscribe to "mytopic/test" and display received message to Serial
//client.subscribe("viewer_click", [](const String & payload) {
// Serial.println(payload);
//});
// Subscribe to "mytopic/wildcardtest/#" and display received message to Serial
//client.subscribe("mytopic/wildcardtest/#", [](const String & topic, const String & payload) {
// Serial.println("(From wildcard) topic: " + topic + ", payload: " + payload);
//});
// Publish a message to "mytopic/test"
client.publish("controller_glove", "alive"); // You can activate the retain flag by setting the third parameter to true
// Execute delayed instructions
//client.executeDelayed(5 * 1000, []() {
// client.publish("mytopic/wildcardtest/test123", "This is a message sent 5 seconds later");
//});
}
Finally in the main loop we call the client handler and button handler. we should be ready to roll with the glove.
void loop()
{
client.loop();
button_handler();
}
Step 7
The first thing we need is a basic flow to separate button presses into different outputs.
[{"id":"50b49ab2.1e6f64","type":"mqtt in","z":"7805686.aa4fd98","name":"","topic":"button_glove","qos":"0","datatype":"auto","broker":"","x":310,"y":2180,"wires":[["ea9f502f.2dd17"]]},{"id":"ea9f502f.2dd17","type":"json","z":"7805686.aa4fd98","name":"","property":"payload","action":"obj","pretty":false,"x":490,"y":2180,"wires":[["425aef92.aa3da8"]]},{"id":"425aef92.aa3da8","type":"function","z":"7805686.aa4fd98","name":"ButtonSeperator","func":"var out = \"\"\nvar count = 0;\nif(msg.payload.b1 != flow.get(\"b1\")){\n if(msg.payload.b1 > flow.get(\"b1\")){\n out = out + \"B1+\";\n }\n flow.set(\"b1\", msg.payload.b1);\n}\nif(msg.payload.b2 != flow.get(\"b2\")){\n if(msg.payload.b2 > flow.get(\"b2\")){\n out = out + \"B2+\";}\n flow.set(\"b2\", msg.payload.b2);\n}\nif(msg.payload.b3 != flow.get(\"b3\")){\n if(msg.payload.b3 > flow.get(\"b3\")){\n out = out + \"B3+\";}\n flow.set(\"b3\", msg.payload.b3);\n}\nif(msg.payload.b4 != flow.get(\"b4\")){\n if(msg.payload.b4 > flow.get(\"b4\")){\n out = out + \"B4+\";}\n flow.set(\"b4\", msg.payload.b4);\n}\nif(msg.payload.b5 != flow.get(\"b5\")){\n if(msg.payload.b5 > flow.get(\"b5\")){\n out = out + \"B5+\";}\n flow.set(\"b5\", msg.payload.b5);\n}\nif(msg.payload.b6 != flow.get(\"b6\")){\n if(msg.payload.b6 > flow.get(\"b6\")){\n out = out + \"B6+\";}\n flow.set(\"b6\", msg.payload.b6);\n}\nif(msg.payload.b7 != flow.get(\"b7\")){\n if(msg.payload.b7 > flow.get(\"b7\")){\n out = out + \"B7+\";}\n flow.set(\"b7\", msg.payload.b7);\n}\nif(msg.payload.b8 != flow.get(\"b8\")){\n if(msg.payload.b8 > flow.get(\"b8\")){\n out = out + \"B8+\";}\n flow.set(\"b8\", msg.payload.b8);\n}\nif(msg.payload.b9 != flow.get(\"b9\")){\n if(msg.payload.b9 > flow.get(\"b9\")){\n out = out + \"B9+\";}\n flow.set(\"b9\", msg.payload.b9);\n}\nif(msg.payload.b10 != flow.get(\"b10\")){\n if(msg.payload.b10 > flow.get(\"b10\")){\n out = out + \"B10+\";}\n flow.set(\"b10\", msg.payload.b10);\n}\nif(msg.payload.b11 != flow.get(\"b11\")){\n if(msg.payload.b11 > flow.get(\"b11\")){\n out = out + \"B11+\";}\n flow.set(\"b11\", msg.payload.b11);\n}\nif(msg.payload.b12 != flow.get(\"b12\")){\n if(msg.payload.b12 > flow.get(\"b12\")){\n out = out + \"B12+\";}\n flow.set(\"b12\", msg.payload.b12);\n}\nif(msg.payload.b13 != flow.get(\"b13\")){\n if(msg.payload.b13 > flow.get(\"b13\")){\n out = out + \"B13+\";}\n flow.set(\"b13\", msg.payload.b13);\n}\nif(msg.payload.b14 != flow.get(\"b14\")){\n if(msg.payload.b14 > flow.get(\"b14\")){\n out = out + \"B14+\";}\n flow.set(\"b14\", msg.payload.b14);\n}\nif(msg.payload.b15 != flow.get(\"b15\")){\n if(msg.payload.b15 > flow.get(\"b15\")){\n out = out + \"B15+\";}\n flow.set(\"b15\", msg.payload.b15);\n}\ncount = msg.payload.b1 + msg.payload.b2 + msg.payload.b3 + msg.payload.b4 +msg.payload.b5 + msg.payload.b6 + msg.payload.b7 + msg.payload.b8 + msg.payload.b9 + msg.payload.b10 + msg.payload.b11 + msg.payload.b12 + msg.payload.b13 + msg.payload.b14 + msg.payload.b15;\nflow.set('buttonCount', count);\n\nmsg.buttonCount = flow.get('buttonCount');\n\nmsg.payload = out.slice(0,-1);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"// Code added here will be run when the\n// node is being stopped or re-deployed.\nflow.set(\"b1\", undefined);\nflow.set(\"b2\", undefined);\nflow.set(\"b2\", undefined);\nflow.set(\"b3\", undefined);\nflow.set(\"b4\", undefined);\nflow.set(\"b4\", undefined);\nflow.set(\"b5\", undefined);\nflow.set(\"b6\", undefined);\nflow.set(\"b7\", undefined);\nflow.set(\"b8\", undefined);\nflow.set(\"b9\", undefined);\nflow.set(\"b10\", undefined);\nflow.set(\"b11\", undefined);\nflow.set(\"b12\", undefined);\nflow.set(\"b13\", undefined);\nflow.set(\"b14\", undefined);\nflow.set(\"b15\", undefined);","x":670,"y":2180,"wires":[["2dcc1aa3.396d06"]],"info":"This function first sets/resets button state when turning on the glove.\n\nThen if the set/reset values are smaller than the incomming values, create an output string for the switch node. \n\nI can also just use the button numbers in the switch node so I am looking into that. "},{"id":"2dcc1aa3.396d06","type":"switch","z":"7805686.aa4fd98","name":"Page_0","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"B1","vt":"str"},{"t":"eq","v":"B2","vt":"str"},{"t":"eq","v":"B3","vt":"str"},{"t":"eq","v":"B4","vt":"str"},{"t":"eq","v":"B5","vt":"str"},{"t":"eq","v":"B6","vt":"str"},{"t":"eq","v":"B7","vt":"str"},{"t":"eq","v":"B8","vt":"str"},{"t":"eq","v":"B9","vt":"str"},{"t":"eq","v":"B10","vt":"str"},{"t":"eq","v":"B11","vt":"str"},{"t":"eq","v":"B12","vt":"str"},{"t":"eq","v":"B13","vt":"str"},{"t":"eq","v":"B14","vt":"str"},{"t":"eq","v":"B15","vt":"str"},{"t":"eq","v":"B1+B2","vt":"str"}],"checkall":"false","repair":false,"outputs":16,"x":880,"y":2180,"wires":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]}]
There is the code for importing into your node red flow. below is a picture of how it should look.
Do not forget to set your mqtt button to your own mqtt broker!
This script takes an mqtt topic from the glove, converts it to json, parses the button presses to create a string of multiple presses, and seperates them. The mutliple presses allows for combo keys, B1 + B2 pressed at the same time. It also allows for some complex commands such as B1 + B6 switching glove button preset pages.
Now for that obs connection.
The most simple way is to create a json message in accordence to the OBS-websockets protocal and pipe it directly into an obs-raw-reqest node. This is all controlled by button 1 and the code for toggling a source is here.
[{"id":"dea9ecb1.7ae268","type":"mqtt in","z":"7805686.aa4fd98","name":"","topic":"button_glove","qos":"0","datatype":"auto","broker":"","x":190,"y":2220,"wires":[["5268685e.ee181"]]},{"id":"5268685e.ee181","type":"json","z":"7805686.aa4fd98","name":"","property":"payload","action":"obj","pretty":false,"x":370,"y":2220,"wires":[["cedfdd82.66af8"]]},{"id":"cedfdd82.66af8","type":"function","z":"7805686.aa4fd98","name":"ButtonSeperator","func":"var out = \"\"\nvar count = 0;\nif(msg.payload.b1 != flow.get(\"b1\")){\n if(msg.payload.b1 > flow.get(\"b1\")){\n out = out + \"B1+\";\n }\n flow.set(\"b1\", msg.payload.b1);\n}\nif(msg.payload.b2 != flow.get(\"b2\")){\n if(msg.payload.b2 > flow.get(\"b2\")){\n out = out + \"B2+\";}\n flow.set(\"b2\", msg.payload.b2);\n}\nif(msg.payload.b3 != flow.get(\"b3\")){\n if(msg.payload.b3 > flow.get(\"b3\")){\n out = out + \"B3+\";}\n flow.set(\"b3\", msg.payload.b3);\n}\nif(msg.payload.b4 != flow.get(\"b4\")){\n if(msg.payload.b4 > flow.get(\"b4\")){\n out = out + \"B4+\";}\n flow.set(\"b4\", msg.payload.b4);\n}\nif(msg.payload.b5 != flow.get(\"b5\")){\n if(msg.payload.b5 > flow.get(\"b5\")){\n out = out + \"B5+\";}\n flow.set(\"b5\", msg.payload.b5);\n}\nif(msg.payload.b6 != flow.get(\"b6\")){\n if(msg.payload.b6 > flow.get(\"b6\")){\n out = out + \"B6+\";}\n flow.set(\"b6\", msg.payload.b6);\n}\nif(msg.payload.b7 != flow.get(\"b7\")){\n if(msg.payload.b7 > flow.get(\"b7\")){\n out = out + \"B7+\";}\n flow.set(\"b7\", msg.payload.b7);\n}\nif(msg.payload.b8 != flow.get(\"b8\")){\n if(msg.payload.b8 > flow.get(\"b8\")){\n out = out + \"B8+\";}\n flow.set(\"b8\", msg.payload.b8);\n}\nif(msg.payload.b9 != flow.get(\"b9\")){\n if(msg.payload.b9 > flow.get(\"b9\")){\n out = out + \"B9+\";}\n flow.set(\"b9\", msg.payload.b9);\n}\nif(msg.payload.b10 != flow.get(\"b10\")){\n if(msg.payload.b10 > flow.get(\"b10\")){\n out = out + \"B10+\";}\n flow.set(\"b10\", msg.payload.b10);\n}\nif(msg.payload.b11 != flow.get(\"b11\")){\n if(msg.payload.b11 > flow.get(\"b11\")){\n out = out + \"B11+\";}\n flow.set(\"b11\", msg.payload.b11);\n}\nif(msg.payload.b12 != flow.get(\"b12\")){\n if(msg.payload.b12 > flow.get(\"b12\")){\n out = out + \"B12+\";}\n flow.set(\"b12\", msg.payload.b12);\n}\nif(msg.payload.b13 != flow.get(\"b13\")){\n if(msg.payload.b13 > flow.get(\"b13\")){\n out = out + \"B13+\";}\n flow.set(\"b13\", msg.payload.b13);\n}\nif(msg.payload.b14 != flow.get(\"b14\")){\n if(msg.payload.b14 > flow.get(\"b14\")){\n out = out + \"B14+\";}\n flow.set(\"b14\", msg.payload.b14);\n}\nif(msg.payload.b15 != flow.get(\"b15\")){\n if(msg.payload.b15 > flow.get(\"b15\")){\n out = out + \"B15+\";}\n flow.set(\"b15\", msg.payload.b15);\n}\ncount = msg.payload.b1 + msg.payload.b2 + msg.payload.b3 + msg.payload.b4 +msg.payload.b5 + msg.payload.b6 + msg.payload.b7 + msg.payload.b8 + msg.payload.b9 + msg.payload.b10 + msg.payload.b11 + msg.payload.b12 + msg.payload.b13 + msg.payload.b14 + msg.payload.b15;\nflow.set('buttonCount', count);\n\nmsg.buttonCount = flow.get('buttonCount');\n\nmsg.payload = out.slice(0,-1);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\nif(flow.get('buttonCount')=='undefined'){\n flow.set('buttonCount',0);\n}\n","finalize":"// Code added here will be run when the\n// node is being stopped or re-deployed.\nflow.set(\"b1\", undefined);\nflow.set(\"b2\", undefined);\nflow.set(\"b2\", undefined);\nflow.set(\"b3\", undefined);\nflow.set(\"b4\", undefined);\nflow.set(\"b4\", undefined);\nflow.set(\"b5\", undefined);\nflow.set(\"b6\", undefined);\nflow.set(\"b7\", undefined);\nflow.set(\"b8\", undefined);\nflow.set(\"b9\", undefined);\nflow.set(\"b10\", undefined);\nflow.set(\"b11\", undefined);\nflow.set(\"b12\", undefined);\nflow.set(\"b13\", undefined);\nflow.set(\"b14\", undefined);\nflow.set(\"b15\", undefined);","x":550,"y":2220,"wires":[["ab014868.0da45"]],"info":"This function first sets/resets button state when turning on the glove.\n\nThen if the set/reset values are smaller than the incomming values, create an output string for the switch node. \n\nI can also just use the button numbers in the switch node so I am looking into that. "},{"id":"ab014868.0da45","type":"switch","z":"7805686.aa4fd98","name":"Page_0","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"B1","vt":"str"},{"t":"eq","v":"B2","vt":"str"},{"t":"eq","v":"B3","vt":"str"},{"t":"eq","v":"B4","vt":"str"},{"t":"eq","v":"B5","vt":"str"},{"t":"eq","v":"B6","vt":"str"},{"t":"eq","v":"B7","vt":"str"},{"t":"eq","v":"B8","vt":"str"},{"t":"eq","v":"B9","vt":"str"},{"t":"eq","v":"B10","vt":"str"},{"t":"eq","v":"B11","vt":"str"},{"t":"eq","v":"B12","vt":"str"},{"t":"eq","v":"B13","vt":"str"},{"t":"eq","v":"B14","vt":"str"},{"t":"eq","v":"B15","vt":"str"},{"t":"eq","v":"B1+B6","vt":"str"}],"checkall":"false","repair":false,"outputs":16,"x":820,"y":2220,"wires":[["273b972.7fe5068"],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]},{"id":"273b972.7fe5068","type":"obs-raw-request","z":"7805686.aa4fd98","name":"","payload":"payload","payloadType":"msg","obsInstance":"cac3db2f.c8125","x":1220,"y":2120,"wires":[[],[]]},{"id":"2e75cd06.ad39d2","type":"function","z":"7805686.aa4fd98","name":"toggle_rtmp","func":"flow.set(\"toggle_rtmp\", false);\nmsg.payload = {\n \"request-type\": \"SetSceneItemRender\",\n \"scene-name\": \"YOUR_SCENE\",\n \"source\": \"YOUR_SOURCE\",\n \"render\": flow.get(\"toggle_rtmp\")\n };\nreturn msg;","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\nflow.set(\"toggle_rtmp\", true);","finalize":"// Code added here will be run when the\n// node is being stopped or re-deployed.\nflow.set(\"toggle_rtmp\", true);","x":1010,"y":2120,"wires":[[]]},{"id":"cac3db2f.c8125","type":"obs-instance","name":"","host":"localhost","port":"4444"}]
And you've done it! Congrats! Now use your imagination! sky's the limit to what you can create!
Maybe add some dashboard text outputs to create a basic info feed for all that's gloing on!
Comments
Please log in or sign up to comment.