Skip to content

Development specification

Oddbjørn Bakke edited this page Oct 21, 2021 · 5 revisions

The plugin itself is only a translator between the StreamDeck Events and TouchPortal Events, specified in the Elgato SDK and this document.

Protocol

The GoXLR protocol is quite simple, it does connect to a websocket server on path ws://<ipAddress>:6805/?GOXLRApp, and sends simple json objects.
Almost everything is from the SDK, the only things that are GoXLR specific, is the action id's and payloads.

Because the GoXLR connects to the server, you can have only one of them running on a single port. If you want more than one application to be connected, you will need a proxy/hub application to reroute to different ports (ports not possible to change in the official plugin).

Json Documents

A good place to start is looking into the Elgato StreamDeck SDK documentation. There you will find the documentation for the json documents.

Behaviour

Since the StreamDeck is button orientated, and Touch Portal is button-agnostic. The API cannot be used with the same behaviour. Some differences are:

  • A Touch Portal plugin cannot know what is showing on the page at a given time (even with broadcast events).
    • We need to always keep track of every profile change, and every thing in the routing table.
  • We cannot configure a button from the plugin. Ex. change icon in a easy way.
    • This needs to be configured in the Touch Portal Desktop App.
  • We cannot fetch the list of profiles when configuring a button in TP on "opening the list".
    • We can as a respond to another thing changing, ex. a client list, but this is a hack.
    • We can fetch the list of profiles on App connection to the plugin, periodically etc.
  • We cannot get the "current" state of what is configured, or save the state in a unhacky way.

Get profile names

Request

{
  "action":"com.tchelicon.goxlr.profilechange",
  "context":"string value",
  "event":"propertyInspectorDidAppear"
}

Response (after 0.17)

{
  "action":"com.tchelicon.goxlr.profilechange",
  "context":"string value",
  "event":"sendToPropertyInspector",
  "payload":{
    "Profiles":["Default - Red", "Default - Teal Blue", "Default - Vaporwave"]
  }
}

Response (before 0.17)

{
  "action":"com.tchelicon.goXLR.ChangeProfile",
  "context":"string value",
  "event":"sendToPropertyInspector",
  "payload":{
    "Profiles":["Default - Red", "Default - Teal Blue", "Default - Vaporwave"]
  }
}

Profiles: List of all the profiles you can select.

Set profile

Request

{
  "action":"com.tchelicon.goxlr.profilechange",
  "event":"keyUp",
  "payload":{
    "settings":{
      "SelectedProfile":"Default - Red"
    }
  }
}

no response

Set Routing

Request

{
  "action":"com.tchelicon.goxlr.routingtable",
  "event":"keyUp",
  "payload":{
    "settings":{
      "RoutingAction":"Toggle",
      "RoutingInput":"Music",
      "RoutingOutput":"Line Out"
    }
  }
}

RoutingAction: "Turn On", "Turn Off", "Toggle"
RoutingInput: "Mic", "Chat", "Music", "Game", "Console", "Line In", "System", "Samples"
RoutingOutput: "Headphones", "Broadcast Mix", "Line Out", "Chat Mic", "Sampler"

no response

Register for events (state) 0.17.0+

There is two ways of registering for events. Either by replying to a goxlrConnectionEvent or by sending a willAppear event. The context field must be a unique string id for the profile, or the routing. And we would need one registration per item.
Ex. 5 profiles will need either 1 goxlrConnectionEvent, it then gets 5 getSettings events, and need to respond with 5 didReceiveSettings events.

In both situations, the GoXLR App will respond by sending a getSettings event. With either action com.tchelicon.goxlr.profilechange or action com.tchelicon.goxlr.routingtable.

Then the plugin needs to send back a didReceiveSettings event with the actual configuration.

Flow during connection:

  1. Request from GoXLR App:
{
  "device": "GoXLR",
  "event": "goxlrConnectionEvent"
}
  1. Response from Plugin:
{
  "event": "goxlrConnectionEvent",
  "payload": [{
    "action": "com.tchelicon.goxlr.profilechange",
    "context": "Default - Red"
    }, {
      "action": "com.tchelicon.goxlr.routingtable",
      "context": "Mic|Headphones"
    }
  ]
}
  1. Request from GoXLR App:
{
  "action": "com.tchelicon.goxlr.profilechange",
  "event": "getSettings",
  "context": "Default - Red"
}

and

{
  "action": "com.tchelicon.goxlr.routingtable",
  "event": "getSettings",
  "context": "Mic|Headphones"
}
  1. Response from Plugin:
{
  "action": "com.tchelicon.goxlr.profilechange",
  "event": "didReceiveSettings",
  "context": "Default - Red",
  "payload":{
    "settings":{
      "SelectedProfile":"Default - Red"
    }
  }
}

and

{
  "action": "com.tchelicon.goxlr.routingtable",
  "event": "didReceiveSettings",
  "context": "Mic|Headphones",
  "payload":{
    "settings":{
      "RoutingAction":"Toggle",
      "RoutingInput":"Mic",
      "RoutingOutput":"Headphones"
    }
  }
}

State updates:

State updates will now come in as:

{
  "action":"com.tchelicon.goxlr.profilechange",
  "context":"Default - Red",
  "event":"setState",
  "payload":{
    "state":1
  }
}

and

{
  "action":"com.tchelicon.goxlr.routingtable",
  "context":"Mic|Headphones",
  "event":"setState",
  "payload":{
    "state":1
  }
}

Remember though, 1 is StateIsNotSet, and 0 is StateIsSet.

Alternative register/enregister.

Register event:

{
  "action":"com.tchelicon.goxlr.profilechange",
  "context":"Røde - ProCaster Red",
  "event":"willAppear"
}

Fallowed with a getSettings, didReceiveSettings, and setState event.
Warning: This seems to require the run of a propertyInspectorDidAppear before or after of the set context. Else the first result will always be not set state (second result will be correct).

Unregister event:

{
  "action":"com.tchelicon.goxlr.profilechange",
  "context":"Røde - ProCaster Red",
  "event":"willDisappear"
}

Clone this wiki locally