Files
onvif-go/deviceio copy.go
T
2026-01-16 04:11:59 +00:00

913 lines
28 KiB
Go

package onvif
import (
"context"
"encoding/xml"
"errors"
"fmt"
"github.com/0x524a/onvif-go/internal/soap"
)
// Device IO service namespace.
const deviceIONamespace = "http://www.onvif.org/ver10/deviceIO/wsdl"
// Device IO service errors.
var (
// ErrInvalidDigitalInputToken is returned when digital input token is invalid.
ErrInvalidDigitalInputToken = errors.New("invalid digital input token: cannot be empty")
// ErrInvalidVideoOutputToken is returned when video output token is invalid.
ErrInvalidVideoOutputToken = errors.New("invalid video output token: cannot be empty")
// ErrInvalidSerialPortToken is returned when serial port token is invalid.
ErrInvalidSerialPortToken = errors.New("invalid serial port token: cannot be empty")
// ErrInvalidSerialData is returned when serial data is invalid.
ErrInvalidSerialData = errors.New("invalid serial data: cannot be empty")
// ErrDigitalInputConfigNil is returned when digital input config is nil.
ErrDigitalInputConfigNil = errors.New("digital input config cannot be nil")
// ErrSerialPortConfigNil is returned when serial port config is nil.
ErrSerialPortConfigNil = errors.New("serial port config cannot be nil")
// ErrVideoOutputConfigNil is returned when video output config is nil.
ErrVideoOutputConfigNil = errors.New("video output configuration cannot be nil")
// ErrInvalidRelayOutputToken is returned when relay output token is invalid.
ErrInvalidRelayOutputToken = errors.New("invalid relay output token: cannot be empty")
)
// DeviceIOServiceCapabilities represents the capabilities of the device IO service.
type DeviceIOServiceCapabilities struct {
VideoSources int
VideoOutputs int
AudioSources int
AudioOutputs int
RelayOutputs int
SerialPorts int
DigitalInputs int
DigitalInputOptions bool
SerialPortConfiguration bool
}
// DigitalInput represents a digital input.
type DigitalInput struct {
Token string
IdleState DigitalIdleState
}
// DigitalIdleState represents the idle state of a digital input.
type DigitalIdleState string
// Digital idle state constants.
const (
DigitalIdleOpen DigitalIdleState = "open"
DigitalIdleClosed DigitalIdleState = "closed"
)
// VideoOutput represents a video output.
type VideoOutput struct {
Token string
Layout *Layout
Resolution *VideoResolution
RefreshRate float64
AspectRatio string
}
// Layout represents a video output layout.
type Layout struct {
Pane []PaneLayout
Extension interface{}
}
// PaneLayout represents a pane layout.
type PaneLayout struct {
Pane string
Area FloatRectangle
}
// FloatRectangle represents a floating point rectangle.
type FloatRectangle struct {
Bottom float64
Top float64
Right float64
Left float64
}
// SerialPort represents a serial port.
type SerialPort struct {
Token string
Type SerialPortType
}
// SerialPortType represents the type of a serial port.
type SerialPortType string
// Serial port type constants.
const (
SerialPortTypeRS232 SerialPortType = "RS232"
SerialPortTypeRS422 SerialPortType = "RS422"
SerialPortTypeRS485 SerialPortType = "RS485"
SerialPortTypeGeneric SerialPortType = "Generic"
)
// SerialPortConfiguration represents a serial port configuration.
type SerialPortConfiguration struct {
Token string
Type SerialPortType
BaudRate int
ParityBit ParityBit
CharacterLength int
StopBit float64
}
// ParityBit represents the parity bit setting.
type ParityBit string
// Parity bit constants.
const (
ParityNone ParityBit = "None"
ParityOdd ParityBit = "Odd"
ParityEven ParityBit = "Even"
ParityMark ParityBit = "Mark"
ParitySpace ParityBit = "Space"
)
// SerialPortConfigurationOptions represents serial port configuration options.
type SerialPortConfigurationOptions struct {
Token string
BaudRateList []int
ParityBitList []ParityBit
CharacterLengthList []int
StopBitList []float64
}
// DigitalInputConfigurationOptions represents digital input configuration options.
type DigitalInputConfigurationOptions struct {
IdleStateOptions []DigitalIdleState
}
// VideoOutputConfiguration represents a video output configuration.
type VideoOutputConfiguration struct {
Token string
Name string
UseCount int
OutputToken string
ForcePersistence bool
}
// VideoOutputConfigurationOptions represents video output configuration options.
type VideoOutputConfigurationOptions struct {
Name StringRange
OutputTokensAvailable []string
}
// StringRange represents a range of string values.
type StringRange struct {
Min int
Max int
}
// RelayOutputOptions represents relay output configuration options.
type RelayOutputOptions struct {
Token string
Mode []RelayMode
DelayTimes []string
Discrete bool
}
// getDeviceIOEndpoint returns the device IO endpoint.
func (c *Client) getDeviceIOEndpoint() string {
// Device IO typically uses the main device endpoint.
return c.endpoint
}
// GetDeviceIOServiceCapabilities retrieves the capabilities of the device IO service.
func (c *Client) GetDeviceIOServiceCapabilities(ctx context.Context) (*DeviceIOServiceCapabilities, error) {
endpoint := c.getDeviceIOEndpoint()
type GetServiceCapabilities struct {
XMLName xml.Name `xml:"tmd:GetServiceCapabilities"`
Xmlns string `xml:"xmlns:tmd,attr"`
}
type GetServiceCapabilitiesResponse struct {
XMLName xml.Name `xml:"GetServiceCapabilitiesResponse"`
Capabilities struct {
VideoSources int `xml:"VideoSources,attr"`
VideoOutputs int `xml:"VideoOutputs,attr"`
AudioSources int `xml:"AudioSources,attr"`
AudioOutputs int `xml:"AudioOutputs,attr"`
RelayOutputs int `xml:"RelayOutputs,attr"`
SerialPorts int `xml:"SerialPorts,attr"`
DigitalInputs int `xml:"DigitalInputs,attr"`
DigitalInputOptions bool `xml:"DigitalInputOptions,attr"`
SerialPortConfiguration bool `xml:"SerialPortConfiguration,attr"`
} `xml:"Capabilities"`
}
req := GetServiceCapabilities{
Xmlns: deviceIONamespace,
}
var resp GetServiceCapabilitiesResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetDeviceIOServiceCapabilities failed: %w", err)
}
return &DeviceIOServiceCapabilities{
VideoSources: resp.Capabilities.VideoSources,
VideoOutputs: resp.Capabilities.VideoOutputs,
AudioSources: resp.Capabilities.AudioSources,
AudioOutputs: resp.Capabilities.AudioOutputs,
RelayOutputs: resp.Capabilities.RelayOutputs,
SerialPorts: resp.Capabilities.SerialPorts,
DigitalInputs: resp.Capabilities.DigitalInputs,
DigitalInputOptions: resp.Capabilities.DigitalInputOptions,
SerialPortConfiguration: resp.Capabilities.SerialPortConfiguration,
}, nil
}
// GetDigitalInputs retrieves all digital inputs.
func (c *Client) GetDigitalInputs(ctx context.Context) ([]*DigitalInput, error) {
endpoint := c.getDeviceIOEndpoint()
type GetDigitalInputs struct {
XMLName xml.Name `xml:"tmd:GetDigitalInputs"`
Xmlns string `xml:"xmlns:tmd,attr"`
}
type GetDigitalInputsResponse struct {
XMLName xml.Name `xml:"GetDigitalInputsResponse"`
DigitalInputs []struct {
Token string `xml:"token,attr"`
IdleState string `xml:"IdleState,attr"`
} `xml:"DigitalInputs"`
}
req := GetDigitalInputs{
Xmlns: deviceIONamespace,
}
var resp GetDigitalInputsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetDigitalInputs failed: %w", err)
}
inputs := make([]*DigitalInput, len(resp.DigitalInputs))
for i, di := range resp.DigitalInputs {
inputs[i] = &DigitalInput{
Token: di.Token,
IdleState: DigitalIdleState(di.IdleState),
}
}
return inputs, nil
}
// GetDigitalInputConfigurationOptions retrieves digital input configuration options.
func (c *Client) GetDigitalInputConfigurationOptions(ctx context.Context, token string) (*DigitalInputConfigurationOptions, error) {
if token == "" {
return nil, ErrInvalidDigitalInputToken
}
endpoint := c.getDeviceIOEndpoint()
type GetDigitalInputConfigurationOptions struct {
XMLName xml.Name `xml:"tmd:GetDigitalInputConfigurationOptions"`
Xmlns string `xml:"xmlns:tmd,attr"`
Token string `xml:"tmd:Token"`
}
type GetDigitalInputConfigurationOptionsResponse struct {
XMLName xml.Name `xml:"GetDigitalInputConfigurationOptionsResponse"`
DigitalInputConfigurationOptions struct {
IdleState []string `xml:"IdleState"`
} `xml:"DigitalInputConfigurationOptions"`
}
req := GetDigitalInputConfigurationOptions{
Xmlns: deviceIONamespace,
Token: token,
}
var resp GetDigitalInputConfigurationOptionsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetDigitalInputConfigurationOptions failed: %w", err)
}
options := &DigitalInputConfigurationOptions{
IdleStateOptions: make([]DigitalIdleState, len(resp.DigitalInputConfigurationOptions.IdleState)),
}
for i, state := range resp.DigitalInputConfigurationOptions.IdleState {
options.IdleStateOptions[i] = DigitalIdleState(state)
}
return options, nil
}
// SetDigitalInputConfigurations sets digital input configurations.
func (c *Client) SetDigitalInputConfigurations(ctx context.Context, inputs []*DigitalInput) error {
if len(inputs) == 0 {
return ErrDigitalInputConfigNil
}
endpoint := c.getDeviceIOEndpoint()
type DigitalInputXML struct {
Token string `xml:"token,attr"`
IdleState string `xml:"IdleState,attr,omitempty"`
}
type SetDigitalInputConfigurations struct {
XMLName xml.Name `xml:"tmd:SetDigitalInputConfigurations"`
Xmlns string `xml:"xmlns:tmd,attr"`
DigitalInputs []DigitalInputXML `xml:"tmd:DigitalInputs"`
}
type SetDigitalInputConfigurationsResponse struct {
XMLName xml.Name `xml:"SetDigitalInputConfigurationsResponse"`
}
digitalInputsXML := make([]DigitalInputXML, len(inputs))
for i, input := range inputs {
if input.Token == "" {
return ErrInvalidDigitalInputToken
}
digitalInputsXML[i] = DigitalInputXML{
Token: input.Token,
IdleState: string(input.IdleState),
}
}
req := SetDigitalInputConfigurations{
Xmlns: deviceIONamespace,
DigitalInputs: digitalInputsXML,
}
var resp SetDigitalInputConfigurationsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return fmt.Errorf("SetDigitalInputConfigurations failed: %w", err)
}
return nil
}
// GetVideoOutputs retrieves all video outputs.
func (c *Client) GetVideoOutputs(ctx context.Context) ([]*VideoOutput, error) {
endpoint := c.getDeviceIOEndpoint()
type GetVideoOutputs struct {
XMLName xml.Name `xml:"tmd:GetVideoOutputs"`
Xmlns string `xml:"xmlns:tmd,attr"`
}
type GetVideoOutputsResponse struct {
XMLName xml.Name `xml:"GetVideoOutputsResponse"`
VideoOutputs []struct {
Token string `xml:"token,attr"`
Layout *struct {
Pane []struct {
Pane string `xml:"Pane,attr"`
Area struct {
Bottom float64 `xml:"bottom,attr"`
Top float64 `xml:"top,attr"`
Right float64 `xml:"right,attr"`
Left float64 `xml:"left,attr"`
} `xml:"Area"`
} `xml:"Pane"`
} `xml:"Layout"`
Resolution *struct {
Width int `xml:"Width"`
Height int `xml:"Height"`
} `xml:"Resolution"`
RefreshRate float64 `xml:"RefreshRate"`
AspectRatio string `xml:"AspectRatio"`
} `xml:"VideoOutputs"`
}
req := GetVideoOutputs{
Xmlns: deviceIONamespace,
}
var resp GetVideoOutputsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetVideoOutputs failed: %w", err)
}
outputs := make([]*VideoOutput, len(resp.VideoOutputs))
for i, vo := range resp.VideoOutputs {
output := &VideoOutput{
Token: vo.Token,
RefreshRate: vo.RefreshRate,
AspectRatio: vo.AspectRatio,
}
if vo.Resolution != nil {
output.Resolution = &VideoResolution{
Width: vo.Resolution.Width,
Height: vo.Resolution.Height,
}
}
if vo.Layout != nil {
output.Layout = &Layout{
Pane: make([]PaneLayout, len(vo.Layout.Pane)),
}
for j, pane := range vo.Layout.Pane {
output.Layout.Pane[j] = PaneLayout{
Pane: pane.Pane,
Area: FloatRectangle{
Bottom: pane.Area.Bottom,
Top: pane.Area.Top,
Right: pane.Area.Right,
Left: pane.Area.Left,
},
}
}
}
outputs[i] = output
}
return outputs, nil
}
// GetSerialPorts retrieves all serial ports.
func (c *Client) GetSerialPorts(ctx context.Context) ([]*SerialPort, error) {
endpoint := c.getDeviceIOEndpoint()
type GetSerialPorts struct {
XMLName xml.Name `xml:"tmd:GetSerialPorts"`
Xmlns string `xml:"xmlns:tmd,attr"`
}
type GetSerialPortsResponse struct {
XMLName xml.Name `xml:"GetSerialPortsResponse"`
SerialPorts []struct {
Token string `xml:"token,attr"`
Type string `xml:"Type"`
} `xml:"SerialPorts"`
}
req := GetSerialPorts{
Xmlns: deviceIONamespace,
}
var resp GetSerialPortsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetSerialPorts failed: %w", err)
}
ports := make([]*SerialPort, len(resp.SerialPorts))
for i, sp := range resp.SerialPorts {
ports[i] = &SerialPort{
Token: sp.Token,
Type: SerialPortType(sp.Type),
}
}
return ports, nil
}
// GetSerialPortConfiguration retrieves a serial port configuration.
func (c *Client) GetSerialPortConfiguration(ctx context.Context, serialPortToken string) (*SerialPortConfiguration, error) {
if serialPortToken == "" {
return nil, ErrInvalidSerialPortToken
}
endpoint := c.getDeviceIOEndpoint()
type GetSerialPortConfiguration struct {
XMLName xml.Name `xml:"tmd:GetSerialPortConfiguration"`
Xmlns string `xml:"xmlns:tmd,attr"`
SerialPortToken string `xml:"tmd:SerialPortToken"`
}
type GetSerialPortConfigurationResponse struct {
XMLName xml.Name `xml:"GetSerialPortConfigurationResponse"`
SerialPortConfiguration struct {
Token string `xml:"token,attr"`
Type string `xml:"Type"`
BaudRate int `xml:"BaudRate"`
ParityBit string `xml:"ParityBit"`
CharacterLength int `xml:"CharacterLength"`
StopBit float64 `xml:"StopBit"`
} `xml:"SerialPortConfiguration"`
}
req := GetSerialPortConfiguration{
Xmlns: deviceIONamespace,
SerialPortToken: serialPortToken,
}
var resp GetSerialPortConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetSerialPortConfiguration failed: %w", err)
}
return &SerialPortConfiguration{
Token: resp.SerialPortConfiguration.Token,
Type: SerialPortType(resp.SerialPortConfiguration.Type),
BaudRate: resp.SerialPortConfiguration.BaudRate,
ParityBit: ParityBit(resp.SerialPortConfiguration.ParityBit),
CharacterLength: resp.SerialPortConfiguration.CharacterLength,
StopBit: resp.SerialPortConfiguration.StopBit,
}, nil
}
// GetSerialPortConfigurationOptions retrieves serial port configuration options.
func (c *Client) GetSerialPortConfigurationOptions(ctx context.Context, serialPortToken string) (*SerialPortConfigurationOptions, error) {
if serialPortToken == "" {
return nil, ErrInvalidSerialPortToken
}
endpoint := c.getDeviceIOEndpoint()
type GetSerialPortConfigurationOptions struct {
XMLName xml.Name `xml:"tmd:GetSerialPortConfigurationOptions"`
Xmlns string `xml:"xmlns:tmd,attr"`
SerialPortToken string `xml:"tmd:SerialPortToken"`
}
type GetSerialPortConfigurationOptionsResponse struct {
XMLName xml.Name `xml:"GetSerialPortConfigurationOptionsResponse"`
SerialPortConfigurationOptions struct {
Token string `xml:"token,attr"`
BaudRateList []int `xml:"BaudRateList>Items"`
ParityBitList []string `xml:"ParityBitList>Items"`
CharLengthList []int `xml:"CharacterLengthList>Items"`
StopBitList []float64 `xml:"StopBitList>Items"`
} `xml:"SerialPortConfigurationOptions"`
}
req := GetSerialPortConfigurationOptions{
Xmlns: deviceIONamespace,
SerialPortToken: serialPortToken,
}
var resp GetSerialPortConfigurationOptionsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetSerialPortConfigurationOptions failed: %w", err)
}
options := &SerialPortConfigurationOptions{
Token: resp.SerialPortConfigurationOptions.Token,
BaudRateList: resp.SerialPortConfigurationOptions.BaudRateList,
CharacterLengthList: resp.SerialPortConfigurationOptions.CharLengthList,
StopBitList: resp.SerialPortConfigurationOptions.StopBitList,
}
// Convert parity bit strings to ParityBit type.
options.ParityBitList = make([]ParityBit, len(resp.SerialPortConfigurationOptions.ParityBitList))
for i, pb := range resp.SerialPortConfigurationOptions.ParityBitList {
options.ParityBitList[i] = ParityBit(pb)
}
return options, nil
}
// SetSerialPortConfiguration sets a serial port configuration.
func (c *Client) SetSerialPortConfiguration(ctx context.Context, config *SerialPortConfiguration) error {
if config == nil {
return ErrSerialPortConfigNil
}
if config.Token == "" {
return ErrInvalidSerialPortToken
}
endpoint := c.getDeviceIOEndpoint()
type SerialPortConfigurationXML struct {
Token string `xml:"token,attr"`
Type string `xml:"tmd:Type"`
BaudRate int `xml:"tmd:BaudRate"`
ParityBit string `xml:"tmd:ParityBit"`
CharacterLength int `xml:"tmd:CharacterLength"`
StopBit float64 `xml:"tmd:StopBit"`
}
type SetSerialPortConfiguration struct {
XMLName xml.Name `xml:"tmd:SetSerialPortConfiguration"`
Xmlns string `xml:"xmlns:tmd,attr"`
SerialPortConfiguration SerialPortConfigurationXML `xml:"tmd:SerialPortConfiguration"`
}
type SetSerialPortConfigurationResponse struct {
XMLName xml.Name `xml:"SetSerialPortConfigurationResponse"`
}
req := SetSerialPortConfiguration{
Xmlns: deviceIONamespace,
SerialPortConfiguration: SerialPortConfigurationXML{
Token: config.Token,
Type: string(config.Type),
BaudRate: config.BaudRate,
ParityBit: string(config.ParityBit),
CharacterLength: config.CharacterLength,
StopBit: config.StopBit,
},
}
var resp SetSerialPortConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return fmt.Errorf("SetSerialPortConfiguration failed: %w", err)
}
return nil
}
// SendReceiveSerialCommand sends a serial command and receives a response.
func (c *Client) SendReceiveSerialCommand(ctx context.Context, serialPortToken string, data []byte, timeoutSeconds, dataLength int) ([]byte, error) {
if serialPortToken == "" {
return nil, ErrInvalidSerialPortToken
}
if len(data) == 0 {
return nil, ErrInvalidSerialData
}
endpoint := c.getDeviceIOEndpoint()
type SerialData struct {
Binary string `xml:"tt:Binary,omitempty"`
}
type SendReceiveSerialCommand struct {
XMLName xml.Name `xml:"tmd:SendReceiveSerialCommand"`
Xmlns string `xml:"xmlns:tmd,attr"`
XmlnsTT string `xml:"xmlns:tt,attr"`
Token string `xml:"tmd:Token"`
SerialData SerialData `xml:"tmd:SerialData"`
TimeOut string `xml:"tmd:TimeOut,omitempty"`
DataLength int `xml:"tmd:DataLength,omitempty"`
}
type SendReceiveSerialCommandResponse struct {
XMLName xml.Name `xml:"SendReceiveSerialCommandResponse"`
SerialData struct {
Binary string `xml:"Binary"`
} `xml:"SerialData"`
}
req := SendReceiveSerialCommand{
Xmlns: deviceIONamespace,
XmlnsTT: "http://www.onvif.org/ver10/schema",
Token: serialPortToken,
SerialData: SerialData{
Binary: string(data),
},
DataLength: dataLength,
}
if timeoutSeconds > 0 {
req.TimeOut = fmt.Sprintf("PT%dS", timeoutSeconds)
}
var resp SendReceiveSerialCommandResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("SendReceiveSerialCommand failed: %w", err)
}
return []byte(resp.SerialData.Binary), nil
}
// GetVideoOutputConfiguration retrieves a video output configuration.
func (c *Client) GetVideoOutputConfiguration(ctx context.Context, videoOutputToken string) (*VideoOutputConfiguration, error) {
if videoOutputToken == "" {
return nil, ErrInvalidVideoOutputToken
}
endpoint := c.getDeviceIOEndpoint()
type GetVideoOutputConfiguration struct {
XMLName xml.Name `xml:"tmd:GetVideoOutputConfiguration"`
Xmlns string `xml:"xmlns:tmd,attr"`
VideoOutputToken string `xml:"tmd:VideoOutputToken"`
}
type GetVideoOutputConfigurationResponse struct {
XMLName xml.Name `xml:"GetVideoOutputConfigurationResponse"`
VideoOutputConfiguration struct {
Token string `xml:"token,attr"`
Name string `xml:"Name"`
UseCount int `xml:"UseCount"`
OutputToken string `xml:"OutputToken"`
} `xml:"VideoOutputConfiguration"`
}
req := GetVideoOutputConfiguration{
Xmlns: deviceIONamespace,
VideoOutputToken: videoOutputToken,
}
var resp GetVideoOutputConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetVideoOutputConfiguration failed: %w", err)
}
return &VideoOutputConfiguration{
Token: resp.VideoOutputConfiguration.Token,
Name: resp.VideoOutputConfiguration.Name,
UseCount: resp.VideoOutputConfiguration.UseCount,
OutputToken: resp.VideoOutputConfiguration.OutputToken,
}, nil
}
// GetVideoOutputConfigurationOptions retrieves video output configuration options.
func (c *Client) GetVideoOutputConfigurationOptions(ctx context.Context, videoOutputToken string) (*VideoOutputConfigurationOptions, error) {
if videoOutputToken == "" {
return nil, ErrInvalidVideoOutputToken
}
endpoint := c.getDeviceIOEndpoint()
type GetVideoOutputConfigurationOptions struct {
XMLName xml.Name `xml:"tmd:GetVideoOutputConfigurationOptions"`
Xmlns string `xml:"xmlns:tmd,attr"`
VideoOutputToken string `xml:"tmd:VideoOutputToken"`
}
type GetVideoOutputConfigurationOptionsResponse struct {
XMLName xml.Name `xml:"GetVideoOutputConfigurationOptionsResponse"`
VideoOutputConfigurationOptions struct {
Name struct {
Min int `xml:"Min,attr"`
Max int `xml:"Max,attr"`
} `xml:"Name"`
OutputTokensAvailable []string `xml:"OutputTokensAvailable"`
} `xml:"VideoOutputConfigurationOptions"`
}
req := GetVideoOutputConfigurationOptions{
Xmlns: deviceIONamespace,
VideoOutputToken: videoOutputToken,
}
var resp GetVideoOutputConfigurationOptionsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetVideoOutputConfigurationOptions failed: %w", err)
}
return &VideoOutputConfigurationOptions{
Name: StringRange{
Min: resp.VideoOutputConfigurationOptions.Name.Min,
Max: resp.VideoOutputConfigurationOptions.Name.Max,
},
OutputTokensAvailable: resp.VideoOutputConfigurationOptions.OutputTokensAvailable,
}, nil
}
// SetVideoOutputConfiguration sets a video output configuration.
func (c *Client) SetVideoOutputConfiguration(ctx context.Context, config *VideoOutputConfiguration) error {
if config == nil {
return ErrVideoOutputConfigNil
}
if config.Token == "" {
return ErrInvalidVideoOutputToken
}
endpoint := c.getDeviceIOEndpoint()
type VideoOutputConfigurationXML struct {
Token string `xml:"token,attr"`
Name string `xml:"tt:Name"`
UseCount int `xml:"tt:UseCount"`
OutputToken string `xml:"tt:OutputToken"`
}
type SetVideoOutputConfiguration struct {
XMLName xml.Name `xml:"tmd:SetVideoOutputConfiguration"`
Xmlns string `xml:"xmlns:tmd,attr"`
XmlnsTT string `xml:"xmlns:tt,attr"`
Configuration VideoOutputConfigurationXML `xml:"tmd:Configuration"`
ForcePersistence bool `xml:"tmd:ForcePersistence"`
}
type SetVideoOutputConfigurationResponse struct {
XMLName xml.Name `xml:"SetVideoOutputConfigurationResponse"`
}
req := SetVideoOutputConfiguration{
Xmlns: deviceIONamespace,
XmlnsTT: "http://www.onvif.org/ver10/schema",
Configuration: VideoOutputConfigurationXML{
Token: config.Token,
Name: config.Name,
UseCount: config.UseCount,
OutputToken: config.OutputToken,
},
ForcePersistence: config.ForcePersistence,
}
var resp SetVideoOutputConfigurationResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return fmt.Errorf("SetVideoOutputConfiguration failed: %w", err)
}
return nil
}
// GetRelayOutputOptions retrieves relay output options.
func (c *Client) GetRelayOutputOptions(ctx context.Context, relayOutputToken string) (*RelayOutputOptions, error) {
if relayOutputToken == "" {
return nil, ErrInvalidRelayOutputToken
}
endpoint := c.getDeviceIOEndpoint()
type GetRelayOutputOptions struct {
XMLName xml.Name `xml:"tmd:GetRelayOutputOptions"`
Xmlns string `xml:"xmlns:tmd,attr"`
RelayOutputToken string `xml:"tmd:RelayOutputToken"`
}
type GetRelayOutputOptionsResponse struct {
XMLName xml.Name `xml:"GetRelayOutputOptionsResponse"`
RelayOutputOptions struct {
Token string `xml:"token,attr"`
Mode []string `xml:"Mode"`
DelayTimes []string `xml:"DelayTimes"`
Discrete bool `xml:"Discrete"`
} `xml:"RelayOutputOptions"`
}
req := GetRelayOutputOptions{
Xmlns: deviceIONamespace,
RelayOutputToken: relayOutputToken,
}
var resp GetRelayOutputOptionsResponse
username, password := c.GetCredentials()
soapClient := soap.NewClient(c.httpClient, username, password)
if err := soapClient.Call(ctx, endpoint, "", req, &resp); err != nil {
return nil, fmt.Errorf("GetRelayOutputOptions failed: %w", err)
}
modes := make([]RelayMode, len(resp.RelayOutputOptions.Mode))
for i, m := range resp.RelayOutputOptions.Mode {
modes[i] = RelayMode(m)
}
return &RelayOutputOptions{
Token: resp.RelayOutputOptions.Token,
Mode: modes,
DelayTimes: resp.RelayOutputOptions.DelayTimes,
Discrete: resp.RelayOutputOptions.Discrete,
}, nil
}