Coder Social home page Coder Social logo

streamdeckws's Introduction

About

StreamDeckWS is a WebSocket proxy for Corsair Stream Deck.

Now you can send and receive messages for your Stream Deck from remote servers like Node-RED websocket plugin.

Not only you will receive key and other events in Node-RED, but you will also be able to build messages to change button title, image, etc.

Configuration

Simply put the address of the remote server as ws://remote_server (or wss://remote_server if you are using HTTPS/SSL). For Node-RED, it will likely be something like ws://192.168.10.100:1880/sd-demo or whatever websocket path you defined.

screen1

id can be used to identify each button. It can be found in the settings object of the payload to the remote server.

Do not set anything in Title if you want to be able to change title through the remote server

Node-RED

Let's build a sample Node-RED flow that returns a random image every time you push a button.

nodered1

Websocket node

First of all, drag a websocket in node and define a websocket endpoint, for example /sd-demo. Type should be set to Listen on and Send / Receive to payload.

JSON Parser

Then drag a JSON Parser so the input is converted into a JSON object.

Save context

context is a critical element from the request because it will be needed in any message we are sending back to the Stream Deck, so it's a good idea to put it aside for later.

Drag a Change node and configure it like so :

nodered2

That way when the payload is overwritten by subsequent nodes, you can always retrieve the context from the message.

Act on Key up only

The Stream Deck sends two messages for each press : Key down and Key up, in our case, let's say we're only interested in Key up.

Drag a Switch node and configure it like so :

nodered3

Get some image from The Internet

Next we will use a node that is not standard with node red, you will have to install it from the Manage palette. The node you need is node-red-contrib-image-tools.

Then drag an Image node and set the following properties :

nodered4

Format the message back to Stream Deck

Drag a Change node and set msg.payload to the following expression

{
   "event":"setImage",
   "context":context,
   "payload":{
       "image":image
   }
}

Send the message back

Lastly, drag a websocket out node and make sure it is configured for the same endpoint as the websocket in node.

Testing

If you're lucky enough, when you push the button you assigned for the StreamDeckWS plugin with the correct URL to Node-RED, you should see an image replace the default icon, and be replaced every time you push it.

You can even go crazy if you'd like...

nodered5

Going further

Now, wouldn't it be cool if the image appeared initially when the key is displayed ?

Just add a Switch node parallel to the existing Key up node like this :

nodered6

This one must match the willAppear event :

nodered7

Anytime the key becomes visible it will fetch a new image

GIF

Eventually it is all going to be clean and tidy !

Wrapping up

With this module, you can extend Stream Deck with infinite possibillities, imagination is the limit !

Eventually your flows are gonna be clean and tidy like mine !

nodered8

Please share all the great things you are doing with Stream Deck WS !

streamdeckws's People

Contributors

dennisgaida avatar pakerfeldt avatar ybizeul avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

streamdeckws's Issues

Example for SD+ missing

The documentation for the rotary dials (e.g. for setting progress on the bars) is either very well hidden or not to be found. Any chance of adding it to the readme?
Edit: I just understood the stuff with the payloads, sorry. An example would be a great thing nevertheless :)

Image Send Problem

The keystrokes arrive, but sending the picture is really hard for me.
Unfortunately, no errors or anything else come back.

The following code is stored in the example:
{
"event":"setImage",
"context":context,
"payload":{
"image":image
}
}

I send:
{"event":"setImage","context":"test","payload":{"image":"Ej.......8I"}}

Ej.......8I = 20736 Strings -> Base64 string
test = WSProxy ID

I don't see any difference to the original. It could be that the command was declared incorrectly??
The red node environment is not used for communication, but i work with Python.

Support websocket connection with TLS (self signed certificate)

Hi, my Node-RED server uses a self signed certificate for websocket connections over TLS. The server is accessible with a web browser over https. I am using a WSProxy Multistate at stream deck with the following WebSocket url:
wss://<my_nodered_ip>:<my_nodered_port>/streamdeck

The websocket connection without TLS is working fine: data can be exchanged between WSProxy and Node-RED server.
The websocket connection with TLS receives no data at Node-RED (checked by a debug node directly after websocket input).
A wireshark trace shows that the TLS connection is started and data is transmitted encrypted to Node-RED.

Do you support websocket connections over TLS with a self signed certifcate? In future version?

WSProxy inside Multi action

Hello. Have some WSProxy actions inside an multi action, they are not working.
Think the issue is that the websocket is not created, Node-red says it's disconnected.

If i put a WSProxy action with same nedpoint next to the multi action, then the multi action is working, and node-red says it's connected.

SD Software: 4.9.0.13177
WSProxy: 1.1.7

Stream Deck button only updates if an event was sent from the button.

First of all, thank you for your work on this amazing plugin. Sadly, i am having a problem with my node red flow.

My flow receives as input the gosumemory websocket (link: https://github.com/l3lackShark/gosumemory), adds the context, action, event device and payload of the last stream deck event sent and then sends a setTitle event, wtih one of the property of the gosumemory payload as title.
Although the stream deck out node returns that the title as been properly set, it doesnt update on the streamdeck.

After some experimenting, it turns out the button only updates after it sends an event. I have compared every property cached with every property sent by the event, and there is no difference. I am guessing that the button only listen for a few moments after sending an event and then stops listening.

Is there a way for me to constantly update the button without having it send an event to the node red websocket?

Here is the node-red flow in question:

[
    {
        "id": "0abf7bee6de541c5",
        "type": "websocket in",
        "z": "d73ac6438fe4af23",
        "name": "",
        "server": "f78bd0552f21ace0",
        "client": "",
        "x": 470,
        "y": 800,
        "wires": [
            [
                "7771914bae0c4546",
                "e9c7e420791dd0d9"
            ]
        ]
    },
    {
        "id": "12b11d6da553bd31",
        "type": "websocket in",
        "z": "d73ac6438fe4af23",
        "name": "",
        "server": "7eeccd3f1bd7ade7",
        "client": "",
        "x": 140,
        "y": 560,
        "wires": [
            [
                "5c1a0560d30372b3",
                "287150dec732e0c8"
            ]
        ]
    },
    {
        "id": "8192fc5b7f888688",
        "type": "websocket out",
        "z": "d73ac6438fe4af23",
        "name": "",
        "server": "f78bd0552f21ace0",
        "client": "",
        "x": 1980,
        "y": 560,
        "wires": []
    },
    {
        "id": "4f9ac307df38509d",
        "type": "sd-input",
        "z": "d73ac6438fe4af23",
        "name": "Stream Deck In",
        "x": 1380,
        "y": 560,
        "wires": [
            [
                "0b17778dd4f0133e"
            ]
        ]
    },
    {
        "id": "0b17778dd4f0133e",
        "type": "function",
        "z": "d73ac6438fe4af23",
        "name": "gosu add",
        "func": "const fs = global.get('fsModule');\nlet str = fs.readFileSync(\"C:/Users/yohan/Documents/websocket/files/sr.txt\", 'utf8');\nmsg.sr = str.replace(/(\\r\\n|\\n|\\r)/gm, \"\");\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1560,
        "y": 560,
        "wires": [
            [
                "7212809494cb6a5a",
                "4c173e1c4d61b156",
                "fbdc26cfcbbecc4f"
            ]
        ]
    },
    {
        "id": "e978c52c6ee841ea",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "stream deck context",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1360,
        "y": 640,
        "wires": []
    },
    {
        "id": "7212809494cb6a5a",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "full payload",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1690,
        "y": 640,
        "wires": []
    },
    {
        "id": "019f9ced8cde6efa",
        "type": "file in",
        "z": "d73ac6438fe4af23",
        "name": "",
        "filename": "C:\\Users\\yohan\\Documents\\websocket\\files\\srcontext.txt",
        "filenameType": "str",
        "format": "utf8",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "allProps": false,
        "x": 1050,
        "y": 560,
        "wires": [
            [
                "4f9ac307df38509d",
                "e978c52c6ee841ea"
            ]
        ]
    },
    {
        "id": "cb5a69fe368291c3",
        "type": "file",
        "z": "d73ac6438fe4af23",
        "name": "",
        "filename": "C:\\Users\\yohan\\Documents\\websocket\\files\\sr.txt",
        "filenameType": "str",
        "appendNewline": true,
        "createDir": false,
        "overwriteFile": "true",
        "encoding": "none",
        "x": 620,
        "y": 560,
        "wires": [
            [
                "019f9ced8cde6efa",
                "e11f5f93a01be469"
            ]
        ]
    },
    {
        "id": "7771914bae0c4546",
        "type": "file",
        "z": "d73ac6438fe4af23",
        "name": "",
        "filename": "C:\\Users\\yohan\\Documents\\websocket\\files\\srcontext.txt",
        "filenameType": "str",
        "appendNewline": true,
        "createDir": false,
        "overwriteFile": "true",
        "encoding": "utf8",
        "x": 810,
        "y": 800,
        "wires": [
            []
        ]
    },
    {
        "id": "e11f5f93a01be469",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "sr value cached",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 880,
        "y": 640,
        "wires": []
    },
    {
        "id": "37b206aaa662d850",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "gosu extract funtion",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 490,
        "y": 640,
        "wires": []
    },
    {
        "id": "5c1a0560d30372b3",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "gosu websocket",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 280,
        "y": 640,
        "wires": []
    },
    {
        "id": "287150dec732e0c8",
        "type": "json",
        "z": "d73ac6438fe4af23",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 210,
        "y": 480,
        "wires": [
            [
                "5b1287c9643ffc45",
                "8ce0c2261e47e728"
            ]
        ]
    },
    {
        "id": "78dc2a0d97724006",
        "type": "json",
        "z": "d73ac6438fe4af23",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 430,
        "y": 480,
        "wires": [
            [
                "cb5a69fe368291c3",
                "babc0f08e56a7d9a"
            ]
        ]
    },
    {
        "id": "5b1287c9643ffc45",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 300,
        "y": 400,
        "wires": []
    },
    {
        "id": "babc0f08e56a7d9a",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "debug 2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 540,
        "y": 400,
        "wires": []
    },
    {
        "id": "8ce0c2261e47e728",
        "type": "change",
        "z": "d73ac6438fe4af23",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.menu.bm.stats.SR",
                "tot": "msg",
                "dc": true
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 320,
        "y": 560,
        "wires": [
            [
                "37b206aaa662d850",
                "78dc2a0d97724006"
            ]
        ]
    },
    {
        "id": "4c173e1c4d61b156",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "sr value",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "sr",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1680,
        "y": 700,
        "wires": []
    },
    {
        "id": "3593fb84350011de",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "stream deck output",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1930,
        "y": 640,
        "wires": []
    },
    {
        "id": "fbdc26cfcbbecc4f",
        "type": "sd-output",
        "z": "d73ac6438fe4af23",
        "name": "Stream Deck Out",
        "streamdeckID": "1",
        "title": "sr",
        "title-type": "msg",
        "image": "",
        "image-type": "str",
        "state": "",
        "state-type": "str",
        "x": 1770,
        "y": 560,
        "wires": [
            [
                "3593fb84350011de",
                "8192fc5b7f888688"
            ]
        ]
    },
    {
        "id": "e9c7e420791dd0d9",
        "type": "debug",
        "z": "d73ac6438fe4af23",
        "name": "debug 7",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 680,
        "y": 900,
        "wires": []
    },
    {
        "id": "f78bd0552f21ace0",
        "type": "websocket-listener",
        "path": "/sr",
        "wholemsg": "false"
    },
    {
        "id": "7eeccd3f1bd7ade7",
        "type": "websocket-listener",
        "path": "/gosu",
        "wholemsg": "false"
    }
]

sd-output node sends "setState" instead of "setImage"

Hello again!

I'm trying to work through the example in the README, but using the sd-input/output nodes instead. It appears that sd-output sends setState for anything with an image, but the example uses setImage. The output node doesn't work unless I use a change node afterward and convert the event field back to setImage. Bug? Something I'm missing?

SetImage only works if no default image is defined

I don't know if this is a limitation of StreamDeck or the plugin but if I set a default image in StreamDeck then this plugin is not able to override it. If I klick on "Reset to default" the image set by the plugin is showing up.

Multistate documentation

The plugin offers regular and multistate options, however there's no documentation on how to use it. In NodeRED when I press the button the payload.payload.isInMultiAction is showing false however it does add the state variable with value 1 when the 1st option (left) is selected and 0 when the 2nd option (right) is selected. Is that a bug?

In NodeRED I'm seemingly unable to set the state of the button dynamically. I have a binary sensor that when ON I want 1st state to show and when off the 2nd state. When setting it to trigger on the willAppear event the state doesn't change and remains on the state I left it at when pressing the button manually. How do I change the state dynamically?

NodeRed

Hi,
I use your description for Node Red with the Module "node-red-contrib-streamdeck-ws 0.9.4", but I get the error message from "StreamDeck IN" --> "SyntaxError: Unexpected token o in JSON at position 1"

How to use the sd-input and sd-output nodes?

HI! Great plugin. I'm setting it up to have better logical control of mimoLive from the Stream Deck. I currently use HTTP calls through Keyboard Maestro, but I want better logic for identifying which layers are active, or which sub-components of a layer are active. I can do this in Node-RED, but until discovering your plugin, I hadn't found a good way to get data back and forth between the Stream Deck and Node-RED.

Your docs cover the basic workflow, but I saw there's also sd-input/output nodes. sd-input seems to work, but sd-output says, "message has no streamdeckID." I don't see a place to set that in the Stream Deck software (macOS), nor anywhere else, for that matter. If I set it to the name of the Stream Deck in its software, the output tells me that there's no context for that name.

Where is this set?

stream deck screen saver

Yann. I think I may have found a bug. I have a single button set to play/pause my office sonos. I notice that is the deck goes into screen saver mode, when it comes out of screen saver, it will fire the button.

I have also experienced this if you try and change the background of the screen saver in the settings page, it will fire the button when you save or cancel.

Let me know if you can reproduce.

Doug

Unable to connect to WebSocket

I have a local WebSocket server running and am trying to connect with this plugin. I am able to verify that the server is running as intended and connectable with other apps, postman, etc. StreamDeck is the only one having issues.

The specific error I get is WebSocket connection to 'ws://localhost:53635/streamdeck/websocket' failed: Error during WebSocket handshake: Unexpected response code: 400

Is it a firewall issue? Can streamdecks not connect to local servers?

Thanks!

Possibility to create a specific function for willAppear and setImage

First of all thank you so much for your work.

I was checking the code and found this:

// The connection to this server already exists

// When starting up, all the keys are rapidly sent but the connection is
// not available when keys after the very first one are added, so we
// simply wait a bit and retry the send message

As long as I understand, the first instance of the plugin creates a websocket client and sends its message. Then the other plugin instances wait for that client to be avilable an then all plugin instances use that websocket client to send the messages.
Will be possible to create a start up funcion that makes each plugin instante use its own websocket client so all the the setImage events can load fast on start up. Then close all those clients and perform the current procedure of making all plugin instances to use the same websocket client?

Thanks for your time and consideration
Best regards.

2023-02-20.20-09-32.mp4

WS v2, StreamDeck Plus WS Socket not Stable

I installed your update v2 to use with my StreamDeck Two+ Dials. Thank you for the update.

At first, all was good. But, when I created a new SD profile and copied the Dial to the second profile, the WS Socket connection disconnected.

I could not get "Reconnect" even after removing the second profile/dial.

A new connection was made with a new Web Socket path (a new configuration node).

I repeated the new profile and copy button, and the same problem with WS Socket disconnecting.

StreamDeck buttons work just fine; the problem is isolated to StreamDeck Dials.

Alan Blind

Set GIF as button Image

The node-red-contrib-image-tools doesn't support gif. Is there a way to set the button image to a gif?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.