dhdcontrolapi

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2025 License: MIT Imports: 14 Imported by: 0

README

Go Client Library for the DHD Control API

This package provides a Go client for
DHD Control API.

We built this library to help our customers kickstart their API projects with the DHD Control API, providing a simple and efficient way to interact with DHD devices.

Control API requires DHD XC3/XD3/X3 IP Cores with a minimum firmware version 10.3.

Go Reference

Quick Start

Import Library
import controlapi "github.com/dhd-audio/control-api-go"
Create Client

First prepare the ClientObjects, then pass them to NewClient:

options := controlapi.ClientOptions{
    URL:         "https://[DNS Name or IP]", 
    Token:       "mysecrettoken",
    InsecureTLS: false,
}
client := controlapi.NewClient(&options)

Replace URL and Token by your actual values.

The InsecureTLS option will make the client ignore invalid TLS certificates.

REST API

Get a single value, or a subtree:

result, err := client.Get("/audio/mixers/0/faders/0")

Get the names of all children of a particular node:

result, err := client.GetFlat("/audio/mixers/0/faders/0")

Set a value:

err := client.Set("/audio/mixers/0/faders/0/pfl1", "true")

WebSocket API

The NewWebSocket method establishes an authenticated connection to the WebSocket API, and parses incoming messages, turning them into event structures. You must provide a channel that receives these events. The channel will be closed when the connection is terminated, i.e. no more message will be received.

If nil is provided as the channel, no event notifications will be sent, and only the synchronous request methods can be used (see below).

Example code:

// Create event channel
ch := make(chan any, 100)

// Open WebSocket and authenticate
ws, err := client.NewWebSocket(ch)
if err != nil {
    // handle error, e.g. authentication error
}
defer ws.Close()

// Process events received from WebSocket
for evt := range ch {
    e := evt.(type) {
    case controlapi.NodeValueEvent:
        fmt.Printf("Node %s is now %v\n", e.Path, e.Value)
    // ...
    // Handle other events
    // ...
    }
}

See the "Events" section below for the available events.

Synchronous Requests

While the WebSocket API is asynchronous by nature, the WebSocket object provides a range of methods that send a specific request, tagged with a random message ID, and wait for the reply from the device. They will block until the reply is received, or a timeout occurs.

Get a single value, or a subtree:

val, err := ws.Get("/audio/mixers/0/faders/0/pfl1")

Set a node value, receiving the new (echoed) value from the device:

val, err := ws.Set("/audio/mixers/0/faders/0/pfl1", true)

Subscribe to a node or subtree, asynchronously receiving changes through NodeValueEvent events:

err := ws.Subscribe("/audio/mixers/0")

Unsubscribe again:

err := ws.Unsubscribe("/audio/mixers/0")

Additional methods are available for RPC requests. Please see the package documentation for details.

Raw Synchronous Requests

In Control API, a request is a JSON object that follows a particular structure. Interally, we define the type Request to represent an arbitrary request. When being sent over the WebSockt, the request object is marshalled to JSON.

type Request map[string]any

There are several functions (in requests.go) available that pre-populate requests with the required fields. For example, NewGetRequest sets the method and path fields in the request object:

func NewGetRequest(path string) Request {
	return Request{
		"method": "get",
		"path":   path,
	}
}

But of course you can also build your own request object this way, for example to use functionality that had not been available by the time this version of the package was written.

The WebSocket object provides two methods to send raw requests synchronously, i.e. tag them with a message ID and wait for the reply from the device. It returns the entire reply from the device, unmarshalled into a WebSocketMessage object:

req := controlapi.NewGetRequest("/audio/mixers/0/faders/0/fader")
msg, err := ws.RequestAndWait(req)

Usually you would be interested in the contents of the "payload" field of the reply. Using RequestAndWaitForReply, you can conveniently perform the request, wait for the reply, and extract the payload for further processing:

req := controlapi.NewGetRequest("/audio/mixers/0/faders/0/fader")
payload, err := ws.RequestAndWaitForPayload(req)
Asynchronous Raw Requests

Raw requests can also be sent asynchronously - the Send() function writes the request message to the WebSocket, but returns immediately without waiting for a reply or checking for errors:

req := controlapi.NewGetRequest("/audio/mixers/0/faders/0/fader")
ws.Send(req) // no return value

Replies to this kind of raw requests will be received through the event channel, and must be processed manually.

Events

If a chan any is supplied during the creation of the WebSocket, the Run() method will continously send notifications to that channel. The values sent to that channel are the structs defined in events.go.

In particular, the following events will occur:

  • MessageEvent: Raw JSON message (unmarshalled to map[string]any) as received from the WebSocket.
  • WebSocketMessage (defined in websocket.go) - the same as MessageEvent, but the message is already unmarshalled into a WebSocketMessage object.
  • NodeValueEvent: This event reports the value of a node in the parameter tree. It occurs when the reply to a "get" request is received, as well as spontaneously when the device reports a change of a node in any of the subscribed sub-trees. The event only carries a single node and value. If the device reports multiple nodes and values in a single WebSocket message, a separate NodeValueEvent is generated for each single node.

See events.go for the full reference of all possible events and their fields.

Contributing

If you have added new features or fixed bugs, feel free to create a pull request.

License

Copyright (c) 2025 DHD audio GmbH.

Licensed under MIT License. See LICENSE for the full licensing terms.

Documentation

Overview

Package dhdcontrolapi provides a client for the DHD control API.

Both the WebSocket and REST API are supported.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnexpectedAuthReply is returned when an unexpected reply to an auth request is received.
	ErrUnexpectedAuthReply = errors.New("unexpected reply to auth request")

	// ErrAuthFailed is returned when authentication failed.
	ErrAuthFailed = errors.New("authentication failed")

	// ErrAwaitTimeout is returned when a reply to a request was not received in time.
	ErrAwaitTimeout = errors.New("timeout awaiting reply")
)

Functions

This section is empty.

Types

type AccessEvent

type AccessEvent struct {
	Mixer       int
	AccessGroup int
	Fader       int
}

AccessEvent is a WebSocket event that occurs when the access state of a mixer changes.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents a connection to the DHD control API.

func NewClient

func NewClient(options *ClientOptions) *Client

NewClient creates a new Control API client.

func (*Client) Get

func (c *Client) Get(path string) (any, error)

Get performs a "get" request on the specified path.

func (*Client) GetFlat

func (c *Client) GetFlat(path string) ([]string, error)

GetFlat performs a "get" request on the specified path with the "flat" option, which returns a list of all nodes under the specified path.

func (*Client) NewWebSocket

func (c *Client) NewWebSocket(eventChannel chan any) (*WebSocket, error)

NewWebSocket creates a new WebSocket connection to the DHD Control API.

Any error occuring during connection or authentication phase will be reported through the returned error.

If an (optional) event channel is supplied, the WebSocket will start sending notifications to that channel whenver an event is received and parsed.

The channel will be closed when the connection is terminated.

The caller must call Close on the WebSocket when done.

func (*Client) Options

func (c *Client) Options() ClientOptions

Options returns the options of the client.

func (*Client) Set

func (c *Client) Set(path string, value string) (string, error)

Set performs a "set" request on the specified path, setting the given value. It returns the response from the server, which is the new value of the node.

type ClientOptions

type ClientOptions struct {
	// Base URL of the DHD control API, including the protocol and port, but exluding the /api/ws or /api/rest part.
	// The URL may contain basic authentication credentials, like http://user:pass@host:port.
	URL string

	// Token to authenticate with the DHD control API.
	Token string

	// InsecureTLS allows to disable TLS certificate verification.
	InsecureTLS bool

	// The duration that the WebSocket request methods wait for a reply before timing out.
	// If set to 0, the default timeout of 5 seconds will be used.
	WebSocketReplyTimeout time.Duration
}

ClientOptions represents the options for a ControlAPI client.

type ExtListEntry

type ExtListEntry struct {
	Label      string `json:"label"`
	Color      string `json:"color,omitempty"`
	Background bool   `json:"background,omitempty"`
	Disabled   bool   `json:"disabled,omitempty"`
	Pagination bool   `json:"pagination,omitempty"`
}

ExtListEntry represents an entry in an external button list

type ExtListNotifiedEvent

type ExtListNotifiedEvent struct {
	List  int
	Entry int
	On    bool
}

ExtListNotifiedEvent is a WebSocket event that occurs when the user presses or releases an external button.

type MessageEvent

type MessageEvent struct {
	Message any
}

MessageEvent is a basic event that carries the payload of a WebSocket message

type NodeValueEvent

type NodeValueEvent struct {
	Path  string
	Value any
}

NodeValueEvent is a WebSocket event that occurs when a node value changes, or as a reply to a get request. If the get reply (or update) contains values for multiple nodes, each node will be reported as a separate event.

type Request

type Request map[string]any

Request represents a request to the DHD Control WebSocket API, which is generally a map of string keys to any values, marshalled to a JSON object when sent through the WebSocket.

func NewAuthRequest

func NewAuthRequest(token string) Request

NewAuthRequest creates an authentication request.

func NewExtListRegisterRequest

func NewExtListRegisterRequest(list int) Request

NewExtListRegisterRequest creates an RPC request to register an external button list.

func NewExtListUnregisterRequest

func NewExtListUnregisterRequest(list int) Request

NewExtListUnregisterRequest creates an RPC request to unregister an external button list.

func NewGetAccessRequest

func NewGetAccessRequest() Request

NewGetAccessRequest creates an RPC request to get and subscribe to the access state.

func NewGetRequest

func NewGetRequest(path string) Request

NewGetRequest creates a get request.

func NewRPCRequest

func NewRPCRequest(payload any) Request

NewRPCRequest creates an RPC request with the given payload.

func NewSetExtListRequest

func NewSetExtListRequest(list int, entries []ExtListEntry, selectedEntry int) Request

NewSetExtListRequest creates an RPC request to update an external button list.

func NewSetRequest

func NewSetRequest(path string, payload any) Request

NewSetRequest creates a set request.

func NewSubscribeRequest

func NewSubscribeRequest(path string) Request

NewSubscribeRequest creates a subscribe request.

func NewUnsubscribeRequest

func NewUnsubscribeRequest(path string) Request

NewUnsubscribeRequest creates an unsubscribe request.

func (Request) WithMsgID

func (r Request) WithMsgID(msgID string) Request

WithMsgID creates a new request by adding or setting the message ID.

type WebSocket

type WebSocket struct {
	// contains filtered or unexported fields
}

WebSocket represents a connection to a DHD device via the WebSocket API.

func NewWebSocket

func NewWebSocket(options *ClientOptions) *WebSocket

NewWebSocket creates a new WebSocket connection. The url must be the full URL to the WebSocket API, including the protocol and port, and the "/api/ws" part. http/https are automatically converted to ws/wss.

func (*WebSocket) Close

func (ws *WebSocket) Close() error

Close closes the connection to the DHD device.

func (*WebSocket) Connect

func (ws *WebSocket) Connect() error

Connect establishes a connection to the DHD device, and authenticates if a token is configured.

func (*WebSocket) Get

func (ws *WebSocket) Get(path string) (any, error)

Get sends a "get" request to the DHD device.

This method is synchronous. It sends the request, waits for the reply, and returns the requested value, or an error.

func (*WebSocket) GetAccess

func (ws *WebSocket) GetAccess() (any, error)

GetAccess sends a "getaccess" RPC request to the DHD device.

It returns the current access state of the device, or an error.

The command will also enable subscription for access state changes, which are notified through the event channel as AccessEvent.

This method is synchronous. It sends the request and waits for the reply.

func (*WebSocket) RPC

func (ws *WebSocket) RPC(payload any) (any, error)

RPC sends an RPC request to the DHD device.

This method is synchronous. It sends the request and waits for the reply.

func (*WebSocket) RegisterExtList

func (ws *WebSocket) RegisterExtList(list int) error

RegisterExtList sends a "registerextlist" RPC request to the DHD device.

This method is synchronous. It sends the request and waits for the reply.

func (*WebSocket) RequestAndWait

func (ws *WebSocket) RequestAndWait(request Request) (WebSocketMessage, error)

RequestAndWait sends a request to the DHD device, and waits for a reply.

If no reply is received within awaitReplyTimeout, an error is returned.

func (*WebSocket) RequestAndWaitForPayload

func (ws *WebSocket) RequestAndWaitForPayload(request Request) (any, error)

RequestAndWaitForPayload sends a request to the DHD device, and waits for a reply. On success, the payload will be extracted and returned.

If no reply is received within awaitReplyTimeout, an error is returned.

func (*WebSocket) Run

func (ws *WebSocket) Run(eventChannel chan any)

Run reads messages from the websocket connection and parses them.

If an event channel is supplied, the raw and parsed messages will be sent to that channel. The channel will be closed when the connection is closed.

If no event channel is supplied, the function will only handle awaited replies.

func (*WebSocket) Send

func (ws *WebSocket) Send(request Request)

Send sends a request to the DHD device over the WebSocket connection. The request will be marshalled to JSON.

The message will be sent asynchronously, and the function returns immediately. Any reply from the device will be handled by the Run function and sent to the event channel.

func (*WebSocket) Set

func (ws *WebSocket) Set(path string, payload any) (any, error)

Set sends a "set" request to the DHD device. It returns the new value as confirmed by the device, or an error.

This method is synchronous. It sends the request, waits for the reply, and returns the requested value, or an error.

func (*WebSocket) SetExtList

func (ws *WebSocket) SetExtList(list int, entries []ExtListEntry, selectedEntry int) error

SetExtList sends a "setextlist" RPC request to the DHD device.

This will populate the external button list with the specified entries, and select the specified entry.

This method is synchronous. It sends the request and waits for the reply.

func (*WebSocket) Subscribe

func (ws *WebSocket) Subscribe(path string) error

Subscribe sends a "subscribe" request to the DHD device, and waits for confirmation.

The device will start sending events when any parameters at or below the specified path changes, until the connection is terminated, or an "unsubscribe" request is sent.

Received events will be sent to the event channel as NodeValueEvent.

This method is synchronous. It sends the request and waits for the reply.

func (*WebSocket) UnregisterExtList

func (ws *WebSocket) UnregisterExtList(list int) error

UnregisterExtList sends an "unregisterextlist" RPC request to the DHD device.

This method is synchronous. It sends the request and waits for the reply.

func (*WebSocket) Unsubscribe

func (ws *WebSocket) Unsubscribe(path string) error

Unsubscribe sends an "unsubscribe" request to the DHD device.

This method is synchronous. It sends the request and waits for the reply.

type WebSocketMessage

type WebSocketMessage struct {
	MsgID   string                `mapstructure:"msgID"`
	Method  string                `mapstructure:"method"`
	Path    string                `mapstructure:"path"`
	Payload any                   `mapstructure:"payload"`
	Success bool                  `mapstructure:"success"`
	Error   WebSocketMessageError `mapstructure:"error"`
}

WebSocketMessage is an unmarshalled message received from the API over WebSocket.

Structs of this type are sent to the event channel whenever a well-formed message is received.

type WebSocketMessageError

type WebSocketMessageError struct {
	Code    int    `mapstructure:"code"`
	Message string `mapstructure:"message"`
}

WebSocketMessageError is the "error" field in a WebSocket message received from the API.

It also implements the Error interface, and is being used to signal errors in the various synchronous request methods.

func (WebSocketMessageError) Error

func (e WebSocketMessageError) Error() string

Error implements the error interface

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL