ybizeul / StreamDeckWS

A WebServices proxy to connect an Elgato Stream Deck to a Node-RED server
86 stars 11 forks source link

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

Open oskapt opened 3 years ago

oskapt commented 3 years ago

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?

ybizeul commented 3 years ago

Thanks for your report.

The way it has been implemented is if Title and Image of the node configuration is empty, then it's assumed to be a state change.

The 3 lines are actually a "or" option, but I didn't figure out how to present it in a more obvious way.

Here is a sample flow to illustrate it, let me know if you think there is a bug :

[
   {
      "id":"32d6f437.d2213c",
      "type":"sd-output",
      "z":"d9237648.984d6",
      "name":"Stream Deck",
      "streamdeckID":"",
      "title":"",
      "title-type":"str",
      "image":"image",
      "image-type":"msg",
      "state":"",
      "state-type":"msg",
      "x":910,
      "y":500,
      "wires":[
         [
            "20e1dda7.2282ba"
         ]
      ]
   },
   {
      "id":"fdcc663c.76bfb",
      "type":"sd-input",
      "z":"d9237648.984d6",
      "name":"Stream Deck In",
      "x":420,
      "y":500,
      "wires":[
         [
            "cbb05c5e.2c791",
            "b204211c.10455"
         ]
      ]
   },
   {
      "id":"2bb3dfb8.02822",
      "type":"jimp-image",
      "z":"d9237648.984d6",
      "name":"",
      "data":"https://picsum.photos/144",
      "dataType":"str",
      "ret":"b64",
      "parameter1":"",
      "parameter1Type":"msg",
      "parameter2":"",
      "parameter2Type":"msg",
      "parameter3":"",
      "parameter3Type":"msg",
      "parameter4":"",
      "parameter4Type":"msg",
      "parameter5":"",
      "parameter5Type":"msg",
      "parameter6":"",
      "parameter6Type":"msg",
      "parameter7":"",
      "parameter7Type":"msg",
      "parameter8":"",
      "parameter8Type":"msg",
      "sendProperty":"image",
      "sendPropertyType":"msg",
      "parameterCount":0,
      "jimpFunction":"none",
      "selectedJimpFunction":{
         "name":"none",
         "fn":"none",
         "description":"Just loads the image.",
         "parameters":[

         ]
      },
      "x":770,
      "y":500,
      "wires":[
         [
            "32d6f437.d2213c"
         ]
      ]
   },
   {
      "id":"b00524f.17e97d8",
      "type":"websocket in",
      "z":"d9237648.984d6",
      "name":"",
      "server":"181cef6a.c12fc1",
      "client":"",
      "x":240,
      "y":500,
      "wires":[
         [
            "fdcc663c.76bfb"
         ]
      ]
   },
   {
      "id":"20e1dda7.2282ba",
      "type":"websocket out",
      "z":"d9237648.984d6",
      "name":"",
      "server":"181cef6a.c12fc1",
      "client":"",
      "x":1080,
      "y":500,
      "wires":[

      ]
   },
   {
      "id":"cbb05c5e.2c791",
      "type":"switch",
      "z":"d9237648.984d6",
      "name":"Key Up",
      "property":"payload.event",
      "propertyType":"msg",
      "rules":[
         {
            "t":"eq",
            "v":"keyUp",
            "vt":"str"
         }
      ],
      "checkall":"true",
      "repair":false,
      "outputs":1,
      "x":600,
      "y":500,
      "wires":[
         [
            "2bb3dfb8.02822"
         ]
      ]
   },
   {
      "id":"b204211c.10455",
      "type":"switch",
      "z":"d9237648.984d6",
      "name":"willAppear",
      "property":"payload.event",
      "propertyType":"msg",
      "rules":[
         {
            "t":"eq",
            "v":"willAppear",
            "vt":"str"
         }
      ],
      "checkall":"true",
      "repair":false,
      "outputs":1,
      "x":610,
      "y":540,
      "wires":[
         [
            "2bb3dfb8.02822"
         ]
      ]
   },
   {
      "id":"181cef6a.c12fc1",
      "type":"websocket-listener",
      "path":"/sd-demo1",
      "wholemsg":"false"
   }
]
oskapt commented 3 years ago

As currently written, I can set the event type to setState and also provide a title, and it's ignored without telling me why. Is it ignored because there can only be one event type and you're limited by the Stream Deck? If so, perhaps instead of having "state" and "title" and "image" as fields, there can simply be "event" and "payload" where the payload is of the type expected by the event. That removes the confusion of having 2/3 of the available fields be ignored and should also simplify your control loop.

If you're not limited by the Stream Deck, the best case scenario is to process it on the Deck - if there's an image, set it. If there's a title, set it. If there's a state that is different than the current state set it (or just set it regardless, because it's idempotent).

Even if you are limited, perhaps you can hide that. If there are multiple fields, have the sd-output node send multiple messages. Then it truly becomes a convenience node.

ybizeul commented 3 years ago

It's a good idea I think. As far as I know, a message can only carry one type of event, but I guess I could make so that multiple messages are sent if multiple fields are set.

I agree the current implementation leads to a lot of confusion and needs to be fixed :

oskapt commented 3 years ago

As long as you clearly document what it does, either option is fine. Personally, I favor the "send multiple messages for me" option because it's less work for the user. In my live streams, for example, I have a multi-state button for muting audio. I want to change the state and also change the title to say, "MUTED." I would like to do this with a single node and a single, combined payload. If I need to update the state based on the successful completion of an action (e.g. only change to the muted image if the source is actually muted), then I would update the title and image and use a single-state button.

Any time I interact with a button, I'm going to make at least one change. If I need multiple interactions in one action, I would still like to do the same amount of work with Node-RED (e.g. configure one payload), and have the "send multiple messages" part occur transparently.

There's no direct benefit to me having to code multiple messages, other than understanding that multiple messages are being sent. A node that does that for me is my friend.

Still loving the project, btw. Last night I set it up to show and update QR codes for URLs during the stream, updating the image on the button to be the actual QR code being shown. It's accurate enough that my phone recognizes the QR code within the button and will open the URL. Out of the 27 buttons that I have configured on my Stream Deck XL for the show, 20 of them are using your project to control Streamlabs OBS via Node-RED. Thank you again for building this.

ybizeul commented 3 years ago

That is great feedback !

I just had a revelation, something that I probably thought about while doing this, but forgot.

The actual behaviour is that if you have just the state specified, it'll trigger a state change event for the button. If you have both the state and title or image it will change the image or title for that state but will not change the state of the button, the reasoning is that we're just updating informations but we want the use to be displayed that information when they push the button.

That is necessary so people can choose to update the status of the states without actually changing the state that is displayed, or, they can trigger a state change by just changing the state without setting a value.

To do what you're doing, I sometimes take a different approach, and try to configure anything that is static directly in Stream Deck, like the title or an icon that represents a state. I set both states in Stream Deck, and then handle the keyup event and if flip the state in the answer, leaving to Stream Deck to change icon and test.

The documentation is actually clear about this, as long as you understand Stream Deck SDK, which I shouldn't assume users do

If the configuration specifies only State, then a SetState event is sent.

If State is configured as well as Title or Image then the configured State will be set in payload.state in the event

Not a silver bullet, but just some thoughts to consider when redesigning this, which I will !