-
1DOWNLOAD_CAD
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.
-
2Docker
-
3OBS Websockets
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!
-
4Node-Red-Contrib-OBS-WS
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.
-
5ELECTRICAL and CAD
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, 16, 17, 18, 19, 21, 22, 23, 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.
CAD
I 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.
-
6Arduino Code
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 = {22, 0, false}; Button button9 = {23, 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(); }
-
7NODE-RED CONNECTIONS!
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.
[{"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!
-
8Twitch PUBSUB
I have a handy bunch of node-red nodes for twitch pubsub api!
[{"id":"843461da.45d378","type":"inject","z":"4d7be1f4.b5275","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"{\"type\":\"LISTEN\",\"data\":{\"topics\":[\"community-points-channel-v1.####\", \"channel-bits-events-v1.#####\", \"channel-subscribe-events-v1.#####\"],\"auth_token\":\"######\"}}","payloadType":"json","x":310,"y":220,"wires":[["6ec442e7.d5a06c"]]},{"id":"6ec442e7.d5a06c","type":"websocket out","z":"4d7be1f4.b5275","name":"","server":"","client":"486da522.b31cbc","x":570,"y":160,"wires":[]},{"id":"75bf52a0.2e1d74","type":"websocket in","z":"4d7be1f4.b5275","name":"","server":"","client":"486da522.b31cbc","x":290,"y":280,"wires":[["9991044e.428128"]]},{"id":"9991044e.428128","type":"function","z":"4d7be1f4.b5275","name":"CH_P_REWARDs","func":"var p = JSON.parse(msg.payload);\n//node.warn(p)\nif (p.type == \"RESPONSE\"){\n return null;\n}\nelse if(p.type == \"PONG\"){\n return msg;\n}\nelse if(p.type==\"MESSAGE\"){\n var par = JSON.parse(p.data.message);\n if (p.data.topic==\"channel-subscribe-events-v1.118520887\") {\n msg.payload = par;\n msg.twitch = \"sub\"\n return msg;\n }\n else if (p.data.topic==\"channel-bits-events-v1.118520887\") {\n msg.payload = par;\n msg.twitch = \"bits\";\n return msg;\n }\n else if(p.data.topic ==\"community-points-channel-v1.118520887\"){\n msg.payload = par.data.redemption.reward.title;\n msg.twitch = \"points\"\n return msg;\n }\n}\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":630,"y":280,"wires":[["2854e5be.9cf312"]]},{"id":"994c2baf.14f918","type":"inject","z":"4d7be1f4.b5275","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"299","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"type\": \"PING\"}","payloadType":"json","x":310,"y":400,"wires":[["d632e291.fe30b"]]},{"id":"d632e291.fe30b","type":"websocket out","z":"4d7be1f4.b5275","name":"","server":"","client":"486da522.b31cbc","x":650,"y":400,"wires":[]},{"id":"2854e5be.9cf312","type":"switch","z":"4d7be1f4.b5275","name":"","property":"twitch","propertyType":"msg","rules":[{"t":"eq","v":"points","vt":"str"},{"t":"eq","v":"bits","vt":"str"},{"t":"eq","v":"sub","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":830,"y":280,"wires":[["adc02e55.50b61","93537952.a4c0e"],["1e1e8848.f59f68"],["865e403.c26d4c"]]},{"id":"865e403.c26d4c","type":"function","z":"4d7be1f4.b5275","name":"sub","func":"var usr = msg.payload.display_name;\nvar mess = msg.payload.sub_message.message;\nvar subp = msg.payload.sub_plan;\nvar months = msg.payload.cumulative_months;\nvar gift = msg.payload.is_gift;\nvar gft_usr = msg.payload.recipient_display_name;\nvar sub_months = msg.payload.multi_month_duration;\nvar user_id = msg.payload.user_id;\n\n\nreturn msg; ","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1010,"y":340,"wires":[["7b0c885a.682998","4eef1467.e0c174","1837cda.8ea5432","d23626b9.08cce"]]},{"id":"1e1e8848.f59f68","type":"function","z":"4d7be1f4.b5275","name":"bits","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1010,"y":280,"wires":[["b77523a7.cb101","4e6d21ad.c722e","199cb650.075e3a"]]},{"id":"adc02e55.50b61","type":"function","z":"4d7be1f4.b5275","name":"points","func":"if(msg.payload == \"_TRAIL\"){\n \n}\nelse if(msg.payload == \"_ACTIVATE_ACCELERATOR\"){\n \n}\nelse if(msg.payload == \"_IN_THOUGHT\"){\n \n}\nelse if(msg.payload == \"_CYBER_VIBES\"){\n \n}\nelse if(msg.payload == \"_FREEZE\"){\n \n}\nelse if(msg.payload == \"Backpack Message\"){\n \n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1010,"y":220,"wires":[["b77523a7.cb101","fa958f54.02a828","22d6ce13.51fb52"]]},{"id":"93537952.a4c0e","type":"switch","z":"4d7be1f4.b5275","name":"points","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"_TRAIL","vt":"str"},{"t":"eq","v":"_ACTIVATE_ACCELERATOR","vt":"str"},{"t":"eq","v":"_CYBER_VIBES","vt":"str"},{"t":"eq","v":"_GLITCH","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":1010,"y":140,"wires":[["774f1747.fa7ba"],["e4c364bc.e1de1"],["79ec5d.a1c64ba4"],["a42acfe.82f02b"]]},{"id":"486da522.b31cbc","type":"websocket-client","path":"wss://pubsub-edge.twitch.tv","tls":"","wholemsg":"false"}]
You'll need to put your channel id and oauth token into the inject node and then you should be able to seperate out channel points, subs, and bits! There ya go!
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.