The proxy server module
=======================

OpenSwitcher provides a proxy server application on top of the ATEM python library. It is designed to easily
interface with one or more ATEM switchers using different protocols that are easier to implement for small
tools. It also aims to provide an ATEM emulation layer in the future so more official and unofficial clients
can be connected to the same hardware than the low connection limit in the hardware itself.

Configuration
-------------

The proxy is configured with a toml configuration file. It defines which hardware it should connect to and which
protocol frontends it should host.

.. code-block:: toml

    [[hardware]]
    id = "mini"
    label = "Atem Mini"
    address = "192.168.2.84"

    [[hardware]]
    id = "mini2"
    label = "Local switcher"
    address = "usb"

    [[frontend]]
    type = "http-api"
    bind = ":8080"
    auth = true
    username = "bob"
    password = "hunter2"
    hardware = "mini,mini2"

    [[frontend]]
    type = "status"
    bind = "127.0.0.1:8082"
    auth = false


For every device there needs to be a `[[hardware]]` section that describes the address to connect to, the internal
id and a display label.

The frontends are described in `[[frontend]]` sections and instead of `id` fields their unique identification is
the `bind` field which sets the port and optionally the IP to bind the protcol to.

Frontend sections
^^^^^^^^^^^^^^^^^

The required keys for the frontend sections are `type`, `bind`, `auth` and `hardware`. If `auth` is enabled then
the `username` and `password` argument are also required.

The `auth` setting enables HTTP basic authentication on the webserver for that specific protocol.

The `hardware` argument is a comma seperated list of hardware ids that are exposed in that specific frontend.

The status frontend
^^^^^^^^^^^^^^^^^^^

The status frontend is a webserver that provides only a single page, a html view of the connected hardware with their
status and a list of the frontend configurations that are enabled.

.. image:: images/proxy-status.png
   :width: 600
   :alt: Screenshot of the webpage generated by the status frontend

.. code-block:: toml

    [[frontend]]
    type = "status"
    bind = "127.0.0.1:8082"
    auth = false
    hardware = "mini"


The HTTP API frontend
^^^^^^^^^^^^^^^^^^^^^

The HTTP API frontend will host an api that allows querying the mixer state as JSON objects. It also allows sending
commands by sending POST requests with either a query string, form-encoded data or json encoded data.

.. code-block:: toml

    [[frontend]]
    type = "http-api"
    bind = "127.0.0.1:8080"
    auth = true
    username = "admin"
    password = "verysecure"
    hardware = "mini,secondswitcher,constellation"


A list of the exposed hardware can be retrieved by sending a GET to /

.. code-block:: shell-session

    $ curl http://localhost:8080/
    {
      "hardware": [
        {
          "id": "mini",
          "status": "connected (ATEM Mini Pro fw 2.30)"
        }
      ]
    }

To get data, send a GET request to `/{hardware}/{field}`. The field in this case is the same as the class names
in the API documentation for fields but with the camelcasing replaced with dashes and the word "Field" removed.

To query for a `ProductNameField` the name would be `product-name`, to get the product name of a device with
the `id` set to `mini` you'd send this request:

.. code-block:: shell-session

    $ curl http://localhost:8080/mini/product-name
    {
      "name": "ATEM Mini Pro"
    }


To send a command to the device the same transform applies, but a POST request is sent instead.

.. code-block:: shell-session

   # Send as query in the url
   $ curl -X POST "http://localhost:8080/mini/program-input?index=0&source=2"

   # Send as application/x-www-form-urlencoded
   $ curl -X POST "http://localhost:8080/mini/program-input" -d "index=0&source=2"

   # Send as json
   $ curl -X POST "http://localhost:8080/mini/program-input" -d '{"index": 0, "source": 2}' -H "Content-Type: application/json"

The TCP frontend
^^^^^^^^^^^^^^^^

The TCP frontend is a custom protocol implementation. The TCP protocol works between the OpenSwitcher proxy and the OpenSwitcher
application and is basically the simplified atem USB protocol tunneled over TCP. It also adds authentication and device
multiplexing. Using this protocol it's possible to control any amount of hardware switcheres over a single TCP port and
have as many clients connected as required.

.. code-block:: toml

    [[frontend]]
    type = "tcp"
    bind = "127.0.0.1:8084"
    auth = true
    username = "admin"
    password = "verysecure"
    hardware = "mini,secondswitcher,constellation"

The pyatem library can connect to this frontend by passing an URL to it:

.. code-block:: url

    tcp://username:password@myproxyserver:8084/secondswitcher

The hardware list is also send to clients in the connection phase. The protocol is designed so clients can show
a popup for authentication when it's enabled and show a device selection popup with the labels set in the hardware
section.

The MQTT frontend
^^^^^^^^^^^^^^^^^

The MQTT frontend will connect to an MQTT server and will sync the hardware state to a topic.


.. code-block:: toml

    [[frontend]]
    type = "mqtt"
    connect = "localhost:1883"
    auth = false
    hardware = "mini"
    allow-writes = false
    topic = "atem/{hardware}/{field}"
    topic-subscribe = "atem/{hardware}/set/{field}"

The proxy will send a new message the the configured MQTT topic every time the state of one of the connected
switchers changes. There's also a special field generated called `status` that contains the connection state
to the hardware on the other side of the proxy.

The `topic` setting defines which topic the messages will go to. In this path you can use the `hardware` variable which
will be the label of the switcher that generated the event and the `field` variable which will be the name of the
changed field in the same format as the `The HTTP API frontend`_ above.

The `topic-subscribe` is an optional setting that sets the path the proxy will subscribe to the MQTT broker to receive
messages back. If this is not set the subscribe topic will be the same as the `topic` setting used to send messages.

The `allow-writes` setting defaults to false. If this setting is changed to true it will make the proxy subscribe to
a topic and allow changing the switcher state my sending MQTT messages to that topic.

.. code-block:: shell-session

   # Switch to input 4 using MQTT
   $ mosquitto_pub -t atem/mini/program-input -m '{"index":0, "source": 4}'

   # Switch to a named input on the hardware
   $ mosquitto_pub -t atem/mini/program-input -m '{"index":0, "source": "PC"}'

   # Receive all MQTT messages from the proxy
   $ mosquitto_sub -F '\e[92m%t \e[96m%p\e[0m' -t "atem/#"

The MQTT module uses the MQTTv5 protocol