From 37d8a71395ecd98ce13ca6a5d989a3419f09a4e0 Mon Sep 17 00:00:00 2001 From: cedricve Date: Fri, 4 Jun 2021 21:51:49 +0200 Subject: [PATCH 01/53] rename imports --- .idea/.gitignore | 8 ++++++++ .idea/modules.xml | 8 ++++++++ .idea/onvif.iml | 9 +++++++++ .idea/vcs.xml | 6 ++++++ Device.go | 8 ++++---- Imaging/types.go | 4 ++-- README.md | 2 +- analytics/types.go | 4 ++-- api/api.go | 8 ++++---- api/get_structs.go | 6 +++--- device/types.go | 4 ++-- event/operation.go | 2 +- event/types.go | 2 +- examples/DeviceService.go | 10 +++++----- examples/discovery_test.go | 6 +++--- go.mod | 2 +- media/types.go | 4 ++-- ptz/types.go | 4 ++-- ws-discovery/ws-discovery.go | 2 +- xsd/built_in.go | 2 +- xsd/onvif/onvif.go | 2 +- 21 files changed, 67 insertions(+), 36 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/onvif.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..ce0aa0d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/onvif.iml b/.idea/onvif.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/onvif.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Device.go b/Device.go index a2e175f..191c32e 100644 --- a/Device.go +++ b/Device.go @@ -12,10 +12,10 @@ import ( "strings" "github.com/beevik/etree" - "github.com/use-go/onvif/device" - "github.com/use-go/onvif/gosoap" - "github.com/use-go/onvif/networking" - wsdiscovery "github.com/use-go/onvif/ws-discovery" + "github.com/kerberos-io/onvif/device" + "github.com/kerberos-io/onvif/gosoap" + "github.com/kerberos-io/onvif/networking" + wsdiscovery "github.com/kerberos-io/onvif/ws-discovery" ) //Xlmns XML Scheam diff --git a/Imaging/types.go b/Imaging/types.go index 34742ba..cf0779a 100644 --- a/Imaging/types.go +++ b/Imaging/types.go @@ -1,8 +1,8 @@ package imaging import ( - "github.com/use-go/onvif/xsd" - "github.com/use-go/onvif/xsd/onvif" + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" ) type GetServiceCapabilities struct { diff --git a/README.md b/README.md index ed4b0f7..4ed2d2b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Simple management of onvif IP-devices cameras. onvif is an implementation of ON To install the library, use **go get**: ```go -go get github.com/use-go/onvif +go get github.com/kerberos-io/onvif ``` diff --git a/analytics/types.go b/analytics/types.go index afbeef3..2b9baf6 100644 --- a/analytics/types.go +++ b/analytics/types.go @@ -1,8 +1,8 @@ package analytics import ( - "github.com/use-go/onvif/xsd" - "github.com/use-go/onvif/xsd/onvif" + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" ) type GetSupportedRules struct { diff --git a/api/api.go b/api/api.go index 233585f..397fa26 100644 --- a/api/api.go +++ b/api/api.go @@ -12,10 +12,10 @@ import ( "github.com/beevik/etree" "github.com/gin-gonic/gin" - "github.com/use-go/onvif" - "github.com/use-go/onvif/gosoap" - "github.com/use-go/onvif/networking" - wsdiscovery "github.com/use-go/onvif/ws-discovery" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/gosoap" + "github.com/kerberos-io/onvif/networking" + wsdiscovery "github.com/kerberos-io/onvif/ws-discovery" ) func RunApi() { diff --git a/api/get_structs.go b/api/get_structs.go index 4bb7834..9957fe4 100644 --- a/api/get_structs.go +++ b/api/get_structs.go @@ -3,9 +3,9 @@ package api import ( "errors" - "github.com/use-go/onvif/device" - "github.com/use-go/onvif/media" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif/device" + "github.com/kerberos-io/onvif/media" + "github.com/kerberos-io/onvif/ptz" ) func getPTZStructByName(name string) (interface{}, error) { diff --git a/device/types.go b/device/types.go index 329cddc..79ed755 100644 --- a/device/types.go +++ b/device/types.go @@ -1,8 +1,8 @@ package device import ( - "github.com/use-go/onvif/xsd" - "github.com/use-go/onvif/xsd/onvif" + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" ) type Service struct { diff --git a/event/operation.go b/event/operation.go index a89625b..d6b10d7 100644 --- a/event/operation.go +++ b/event/operation.go @@ -1,7 +1,7 @@ package event import ( - "github.com/use-go/onvif/xsd" + "github.com/kerberos-io/onvif/xsd" ) //GetServiceCapabilities action diff --git a/event/types.go b/event/types.go index 21a8f1d..b15d472 100644 --- a/event/types.go +++ b/event/types.go @@ -1,7 +1,7 @@ package event import ( - "github.com/use-go/onvif/xsd" + "github.com/kerberos-io/onvif/xsd" ) //Address Alias diff --git a/examples/DeviceService.go b/examples/DeviceService.go index 8db0ba6..62a6c93 100644 --- a/examples/DeviceService.go +++ b/examples/DeviceService.go @@ -6,10 +6,10 @@ import ( "log" "net/http" - goonvif "github.com/use-go/onvif" - "github.com/use-go/onvif/device" - "github.com/use-go/onvif/gosoap" - "github.com/use-go/onvif/xsd/onvif" + goonvif "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/device" + "github.com/kerberos-io/onvif/gosoap" + "github.com/kerberos-io/onvif/xsd/onvif" ) const ( @@ -65,7 +65,7 @@ func main() { log.Println(err) } else { /* - You could use https://github.com/use-go/onvif/gosoap for pretty printing response + You could use https://github.com/kerberos-io/onvif/gosoap for pretty printing response */ fmt.Println(gosoap.SoapMessage(readResponse(createUserResponse)).StringIndent()) } diff --git a/examples/discovery_test.go b/examples/discovery_test.go index 630b28b..da1754c 100644 --- a/examples/discovery_test.go +++ b/examples/discovery_test.go @@ -10,9 +10,9 @@ import ( "testing" "github.com/beevik/etree" - "github.com/use-go/onvif" - "github.com/use-go/onvif/device" - discover "github.com/use-go/onvif/ws-discovery" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/device" + discover "github.com/kerberos-io/onvif/ws-discovery" ) func TestGetAvailableDevicesAtSpecificEthernetInterface(t *testing.T) { diff --git a/go.mod b/go.mod index 30ab34e..ca1c8f3 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/use-go/onvif +module github.com/kerberos-io/onvif go 1.15 diff --git a/media/types.go b/media/types.go index cc80562..4766660 100644 --- a/media/types.go +++ b/media/types.go @@ -1,8 +1,8 @@ package media import ( - "github.com/use-go/onvif/xsd" - "github.com/use-go/onvif/xsd/onvif" + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" ) type Capabilities struct { diff --git a/ptz/types.go b/ptz/types.go index 3efdee1..c5317ca 100644 --- a/ptz/types.go +++ b/ptz/types.go @@ -1,8 +1,8 @@ package ptz import ( - "github.com/use-go/onvif/xsd" - "github.com/use-go/onvif/xsd/onvif" + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" ) type Capabilities struct { diff --git a/ws-discovery/ws-discovery.go b/ws-discovery/ws-discovery.go index dc45a0b..e6c6293 100644 --- a/ws-discovery/ws-discovery.go +++ b/ws-discovery/ws-discovery.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/beevik/etree" - "github.com/use-go/onvif/gosoap" + "github.com/kerberos-io/onvif/gosoap" ) func buildProbeMessage(uuidV4 string, scopes, types []string, nmsp map[string]string) gosoap.SoapMessage { diff --git a/xsd/built_in.go b/xsd/built_in.go index b2a015f..ffea950 100644 --- a/xsd/built_in.go +++ b/xsd/built_in.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/use-go/onvif/xsd/iso8601" + "github.com/kerberos-io/onvif/xsd/iso8601" ) /* diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index e0aca6e..8210695 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -1,7 +1,7 @@ package onvif import ( - "github.com/use-go/onvif/xsd" + "github.com/kerberos-io/onvif/xsd" ) // BUG(r): Enum types implemented as simple string From 9d65ca2b49010a9341e3be023b359a8c2536e6b4 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Fri, 23 Dec 2022 08:55:22 +0100 Subject: [PATCH 02/53] add new types for independent zoom and pan tilt --- ptz/types.go | 10 +++++----- xsd/onvif/onvif.go | 26 ++++++++++++++++---------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ptz/types.go b/ptz/types.go index c5317ca..6c56fb0 100644 --- a/ptz/types.go +++ b/ptz/types.go @@ -145,17 +145,17 @@ type ContinuousMove struct { XMLName string `xml:"tptz:ContinuousMove"` ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` Velocity onvif.PTZSpeed `xml:"tptz:Velocity"` - Timeout xsd.Duration `xml:"tptz:Timeout"` + //Timeout xsd.Duration `xml:"tptz:Timeout"` } type ContinuousMoveResponse struct { } type RelativeMove struct { - XMLName string `xml:"tptz:RelativeMove"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` - Translation onvif.PTZVector `xml:"tptz:Translation"` - Speed onvif.PTZSpeed `xml:"tptz:Speed"` + XMLName string `xml:"tptz:RelativeMove,omitempty"` + ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken,omitempty"` + Translation onvif.PTZVector `xml:"tptz:Translation,omitempty"` + Speed onvif.PTZSpeed `xml:"tptz:Speed,omitempty"` } type RelativeMoveResponse struct { diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index 8210695..9576970 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -547,20 +547,26 @@ type PTZConfiguration struct { Extension PTZConfigurationExtension `xml:"Extension"` } -type PTZSpeed struct { - PanTilt Vector2D `xml:"onvif:PanTilt"` - Zoom Vector1D `xml:"onvif:Zoom"` +type PTZSpeed interface { +} + +type PTZSpeedZoom struct { + Zoom Vector1D `xml:"onvif:Zoom,omitempty"` +} + +type PTZSpeedPanTilt struct { + PanTilt Vector2D `xml:"onvif:PanTilt,omitempty"` } type Vector2D struct { - X float64 `xml:"x,attr"` - Y float64 `xml:"y,attr"` - Space xsd.AnyURI `xml:"space,attr"` + X float64 `xml:"x,attr,omitempty"` + Y float64 `xml:"y,attr,omitempty"` + Space xsd.AnyURI `xml:"space,attr,omitempty"` } type Vector1D struct { - X float64 `xml:"x,attr"` - Space xsd.AnyURI `xml:"space,attr"` + X float64 `xml:"x,attr,omitempty"` + Space xsd.AnyURI `xml:"space,attr,omitempty"` } type PanTiltLimits struct { @@ -1013,8 +1019,8 @@ type PTZPreset struct { } type PTZVector struct { - PanTilt Vector2D `xml:"onvif:PanTilt"` - Zoom Vector1D `xml:"onvif:Zoom"` + PanTilt Vector2D `xml:"onvif:PanTilt,omitempty"` + Zoom Vector1D `xml:"onvif:Zoom,omitempty"` } type PTZStatus struct { From 23e1c0b1bcb6b2f768122ed3c9082d2ed413f418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Verstraeten?= Date: Wed, 25 Jan 2023 14:10:53 +0100 Subject: [PATCH 03/53] Update onvif.go --- xsd/onvif/onvif.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index 9576970..b8ee1dc 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -551,7 +551,7 @@ type PTZSpeed interface { } type PTZSpeedZoom struct { - Zoom Vector1D `xml:"onvif:Zoom,omitempty"` + Zoom Vector1D `xml:"onvif:Zoom"` } type PTZSpeedPanTilt struct { @@ -559,13 +559,13 @@ type PTZSpeedPanTilt struct { } type Vector2D struct { - X float64 `xml:"x,attr,omitempty"` - Y float64 `xml:"y,attr,omitempty"` + X float64 `xml:"x,attr"` + Y float64 `xml:"y,attr"` Space xsd.AnyURI `xml:"space,attr,omitempty"` } type Vector1D struct { - X float64 `xml:"x,attr,omitempty"` + X float64 `xml:"x,attr"` Space xsd.AnyURI `xml:"space,attr,omitempty"` } From c037b63bad44d18535c6295f1432ded841aaabb4 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Thu, 24 Aug 2023 12:36:15 +0200 Subject: [PATCH 04/53] Fix for ONVIF preset (json) --- README.md | 20 ++++++------- xsd/onvif/onvif.go | 70 +++++++++++++++++++++++----------------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 4ed2d2b..232487e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# onvif protocol +# ONVIF protocol -Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard. +Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard. ## Installation -To install the library, use **go get**: +To install the library, use **go get**: ```go go get github.com/kerberos-io/onvif @@ -28,20 +28,20 @@ The following services are implemented: ### General concept -1) Connecting to the device -2) Authentication (if necessary) -3) Defining Data Types -4) Carrying out the required method +1. Connecting to the device +2. Authentication (if necessary) +3. Defining Data Types +4. Carrying out the required method #### Connecting to the device -If there is a device on the network at the address *192.168.13.42*, and its ONVIF services use the *1234* port, then you can connect to the device in the following way: +If there is a device on the network at the address _192.168.13.42_, and its ONVIF services use the _1234_ port, then you can connect to the device in the following way: ```go dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234"}) ``` -*The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. **Usually this is 80 port.*** +\*The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. **Usually this is 80 port.\*** #### Authentication @@ -75,7 +75,7 @@ The figure below shows that `GetServiceCapabilities` does not accept any argumen ![PTZ GetServiceCapabilities](docs/img/GetServiceCapabilities.png) -*Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package.* +_Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package._ An example of how to define the data type of the CreateUsers function in [Devicemgmt](https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl): diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index b8ee1dc..2862119 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -468,10 +468,10 @@ type IPAddress struct { type IPType xsd.String -//IPv4 address +// IPv4 address type IPv4Address xsd.Token -//IPv6 address +// IPv6 address type IPv6Address xsd.Token type AudioEncoderConfiguration struct { @@ -825,7 +825,7 @@ type Transport struct { Tunnel *Transport `xml:"onvif:Tunnel"` } -//enum +// enum type TransportProtocol xsd.String type MediaUri struct { @@ -952,7 +952,7 @@ type PTZSpaces struct { type PTZSpacesExtension xsd.AnyType -//TODO: restriction +// TODO: restriction type AuxiliaryData xsd.String type PTZNodeExtension struct { @@ -1019,15 +1019,15 @@ type PTZPreset struct { } type PTZVector struct { - PanTilt Vector2D `xml:"onvif:PanTilt,omitempty"` - Zoom Vector1D `xml:"onvif:Zoom,omitempty"` + PanTilt Vector2D `xml:"PanTilt,omitempty"` + Zoom Vector1D `xml:"Zoom,omitempty"` } type PTZStatus struct { - Position PTZVector - MoveStatus PTZMoveStatus - Error string - UtcTime xsd.DateTime + Position PTZVector `xml:"Position"` + MoveStatus PTZMoveStatus `xml:"MoveStatus"` + Error string `xml:"Error"` + UtcTime xsd.DateTime `xml:"UtcTime"` } type PTZMoveStatus struct { @@ -1219,7 +1219,7 @@ type UserExtension xsd.String type CapabilityCategory xsd.String -//Capabilities of device +// Capabilities of device type Capabilities struct { Analytics AnalyticsCapabilities Device DeviceCapabilities @@ -1230,14 +1230,14 @@ type Capabilities struct { Extension CapabilitiesExtension } -//AnalyticsCapabilities Check +// AnalyticsCapabilities Check type AnalyticsCapabilities struct { XAddr xsd.AnyURI RuleSupport xsd.Boolean AnalyticsModuleSupport xsd.Boolean } -//DeviceCapabilities Check +// DeviceCapabilities Check type DeviceCapabilities struct { XAddr xsd.AnyURI Network NetworkCapabilities @@ -1247,7 +1247,7 @@ type DeviceCapabilities struct { Extension DeviceCapabilitiesExtension } -//NetworkCapabilities Check +// NetworkCapabilities Check type NetworkCapabilities struct { IPFilter xsd.Boolean ZeroConfiguration xsd.Boolean @@ -1256,16 +1256,16 @@ type NetworkCapabilities struct { Extension NetworkCapabilitiesExtension } -//NetworkCapabilitiesExtension Check +// NetworkCapabilitiesExtension Check type NetworkCapabilitiesExtension struct { Dot11Configuration xsd.Boolean Extension NetworkCapabilitiesExtension2 } -//NetworkCapabilitiesExtension2 Extension2 +// NetworkCapabilitiesExtension2 Extension2 type NetworkCapabilitiesExtension2 xsd.AnyType -//SystemCapabilities check +// SystemCapabilities check type SystemCapabilities struct { DiscoveryResolve xsd.Boolean DiscoveryBye xsd.Boolean @@ -1460,7 +1460,7 @@ type DynamicDNSInformation struct { Extension DynamicDNSInformationExtension } -//TODO: enumeration +// TODO: enumeration type DynamicDNSType xsd.String type DynamicDNSInformationExtension xsd.AnyType @@ -1497,7 +1497,7 @@ type NetworkInterfaceConnectionSetting struct { Duplex Duplex `xml:"onvif:Duplex"` } -//TODO: enum +// TODO: enum type Duplex xsd.String type NetworkInterfaceExtension struct { @@ -1539,19 +1539,19 @@ type Dot11PSKPassphrase xsd.String type Dot11PSK xsd.HexBinary -//TODO: enumeration +// TODO: enumeration type Dot11Cipher xsd.String -//TODO: enumeration +// TODO: enumeration type Dot11SecurityMode xsd.String -//TODO: restrictions +// TODO: restrictions type NetworkInterfaceConfigPriority xsd.Integer -//TODO: enumeration +// TODO: enumeration type Dot11StationMode xsd.String -//TODO: restrictions +// TODO: restrictions type Dot11SSIDType xsd.HexBinary type Dot3Configuration xsd.String @@ -1578,7 +1578,7 @@ type PrefixedIPv6Address struct { PrefixLength xsd.Int `xml:"onvif:PrefixLength"` } -//TODO: enumeration +// TODO: enumeration type IPv6DHCPConfiguration xsd.String type IPv4NetworkInterface struct { @@ -1593,7 +1593,7 @@ type IPv4Configuration struct { DHCP xsd.Boolean } -//optional, unbounded +// optional, unbounded type PrefixedIPv4Address struct { Address IPv4Address `xml:"onvif:Address"` PrefixLength xsd.Int `xml:"onvif:PrefixLength"` @@ -1638,7 +1638,7 @@ type NetworkProtocol struct { type NetworkProtocolExtension xsd.AnyType -//TODO: enumeration +// TODO: enumeration type NetworkProtocolType xsd.String type NetworkGateway struct { @@ -1669,11 +1669,11 @@ type IPAddressFilter struct { type IPAddressFilterExtension xsd.AnyType -//enum { 'Allow', 'Deny' } -//TODO: enumeration +// enum { 'Allow', 'Deny' } +// TODO: enumeration type IPAddressFilterType xsd.String -//TODO: attribite +// TODO: attribite type BinaryData struct { X ContentType `xml:"xmime:contentType,attr"` Data xsd.Base64Binary `xml:"onvif:Data"` @@ -1700,13 +1700,13 @@ type RelayOutputSettings struct { IdleState RelayIdleState `xml:"onvif:IdleState"` } -//TODO:enumeration +// TODO:enumeration type RelayIdleState xsd.String -//TODO: enumeration +// TODO: enumeration type RelayMode xsd.String -//TODO: enumeration +// TODO: enumeration type RelayLogicalState xsd.String type CertificateWithPrivateKey struct { @@ -1782,7 +1782,7 @@ type Dot11Status struct { ActiveConfigAlias ReferenceToken } -//TODO: enumeration +// TODO: enumeration type Dot11SignalStrength xsd.String type Dot11AvailableNetworks struct { @@ -1797,7 +1797,7 @@ type Dot11AvailableNetworks struct { type Dot11AvailableNetworksExtension xsd.AnyType -//TODO: enumeration +// TODO: enumeration type Dot11AuthAndMangementSuite xsd.String type SystemLogUriList struct { From 7903bf271085cac3817eaf9cd4ee2e43b0e27c59 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Wed, 30 Aug 2023 11:01:24 +0200 Subject: [PATCH 05/53] makes presets an array, we have more than a single preset --- ptz/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptz/types.go b/ptz/types.go index 6c56fb0..444e46d 100644 --- a/ptz/types.go +++ b/ptz/types.go @@ -91,7 +91,7 @@ type GetPresets struct { } type GetPresetsResponse struct { - Preset onvif.PTZPreset + Preset []onvif.PTZPreset } type SetPreset struct { From 71b6d4839317d23d8e530135e1edf713f1a42514 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Wed, 6 Dec 2023 20:38:25 +0100 Subject: [PATCH 06/53] diz memory leak + import --- Device.go | 48 +++-- examples/DeviceService.go | 10 +- sdk/codegen/main.go | 6 +- sdk/device/AddIPAddressFilter_auto.go | 6 +- sdk/device/AddScopes_auto.go | 6 +- sdk/device/CreateCertificate_auto.go | 6 +- sdk/device/CreateDot1XConfiguration_auto.go | 6 +- sdk/device/CreateStorageConfiguration_auto.go | 6 +- sdk/device/CreateUsers_auto.go | 6 +- sdk/device/DeleteCertificates_auto.go | 6 +- sdk/device/DeleteDot1XConfiguration_auto.go | 6 +- sdk/device/DeleteGeoLocation_auto.go | 6 +- sdk/device/DeleteStorageConfiguration_auto.go | 6 +- sdk/device/DeleteUsers_auto.go | 6 +- sdk/device/GetAccessPolicy_auto.go | 6 +- sdk/device/GetCACertificates_auto.go | 6 +- sdk/device/GetCapabilities_auto.go | 6 +- sdk/device/GetCertificateInformation_auto.go | 6 +- sdk/device/GetCertificatesStatus_auto.go | 6 +- sdk/device/GetCertificates_auto.go | 6 +- sdk/device/GetClientCertificateMode_auto.go | 6 +- sdk/device/GetDNS_auto.go | 6 +- sdk/device/GetDPAddresses_auto.go | 6 +- sdk/device/GetDeviceInformation_auto.go | 6 +- sdk/device/GetDiscoveryMode_auto.go | 6 +- sdk/device/GetDot11Capabilities_auto.go | 6 +- sdk/device/GetDot11Status_auto.go | 6 +- sdk/device/GetDot1XConfiguration_auto.go | 6 +- sdk/device/GetDot1XConfigurations_auto.go | 6 +- sdk/device/GetDynamicDNS_auto.go | 6 +- sdk/device/GetEndpointReference_auto.go | 6 +- sdk/device/GetGeoLocation_auto.go | 6 +- sdk/device/GetHostname_auto.go | 6 +- sdk/device/GetIPAddressFilter_auto.go | 6 +- sdk/device/GetNTP_auto.go | 6 +- sdk/device/GetNetworkDefaultGateway_auto.go | 6 +- sdk/device/GetNetworkInterfaces_auto.go | 6 +- sdk/device/GetNetworkProtocols_auto.go | 6 +- sdk/device/GetPkcs10Request_auto.go | 6 +- sdk/device/GetRelayOutputs_auto.go | 6 +- sdk/device/GetRemoteDiscoveryMode_auto.go | 6 +- sdk/device/GetRemoteUser_auto.go | 6 +- sdk/device/GetScopes_auto.go | 6 +- sdk/device/GetServiceCapabilities_auto.go | 6 +- sdk/device/GetServices_auto.go | 6 +- sdk/device/GetStorageConfiguration_auto.go | 6 +- sdk/device/GetStorageConfigurations_auto.go | 6 +- sdk/device/GetSystemBackup_auto.go | 6 +- sdk/device/GetSystemDateAndTime_auto.go | 6 +- sdk/device/GetSystemLog_auto.go | 6 +- .../GetSystemSupportInformation_auto.go | 6 +- sdk/device/GetSystemUris_auto.go | 6 +- sdk/device/GetUsers_auto.go | 6 +- sdk/device/GetWsdlUrl_auto.go | 6 +- sdk/device/GetZeroConfiguration_auto.go | 6 +- sdk/device/LoadCACertificates_auto.go | 6 +- .../LoadCertificateWithPrivateKey_auto.go | 6 +- sdk/device/LoadCertificates_auto.go | 6 +- sdk/device/RemoveIPAddressFilter_auto.go | 6 +- sdk/device/RemoveScopes_auto.go | 6 +- sdk/device/RestoreSystem_auto.go | 6 +- sdk/device/ScanAvailableDot11Networks_auto.go | 6 +- sdk/device/SendAuxiliaryCommand_auto.go | 6 +- sdk/device/SetAccessPolicy_auto.go | 6 +- sdk/device/SetCertificatesStatus_auto.go | 6 +- sdk/device/SetClientCertificateMode_auto.go | 6 +- sdk/device/SetDNS_auto.go | 6 +- sdk/device/SetDiscoveryMode_auto.go | 6 +- sdk/device/SetDot1XConfiguration_auto.go | 6 +- sdk/device/SetDynamicDNS_auto.go | 6 +- sdk/device/SetGeoLocation_auto.go | 6 +- sdk/device/SetHostnameFromDHCP_auto.go | 6 +- sdk/device/SetHostname_auto.go | 6 +- sdk/device/SetIPAddressFilter_auto.go | 6 +- sdk/device/SetNTP_auto.go | 6 +- sdk/device/SetNetworkDefaultGateway_auto.go | 6 +- sdk/device/SetNetworkInterfaces_auto.go | 6 +- sdk/device/SetNetworkProtocols_auto.go | 6 +- sdk/device/SetRelayOutputSettings_auto.go | 6 +- sdk/device/SetRelayOutputState_auto.go | 6 +- sdk/device/SetRemoteDiscoveryMode_auto.go | 6 +- sdk/device/SetRemoteUser_auto.go | 6 +- sdk/device/SetScopes_auto.go | 6 +- sdk/device/SetStorageConfiguration_auto.go | 6 +- sdk/device/SetSystemDateAndTime_auto.go | 6 +- sdk/device/SetSystemFactoryDefault_auto.go | 6 +- sdk/device/SetUser_auto.go | 6 +- sdk/device/SetZeroConfiguration_auto.go | 6 +- sdk/device/StartFirmwareUpgrade_auto.go | 6 +- sdk/device/StartSystemRestore_auto.go | 6 +- sdk/device/SystemReboot_auto.go | 6 +- sdk/device/UpgradeSystemFirmware_auto.go | 6 +- sdk/device/device.go | 178 +++++++++--------- sdk/event/CreatePullPointSubscription_auto.go | 6 +- sdk/event/GetEventProperties_auto.go | 6 +- sdk/event/GetServiceCapabilities_auto.go | 6 +- sdk/event/Subscribe_auto.go | 6 +- sdk/event/Unsubscribe_auto.go | 6 +- .../AddAudioDecoderConfiguration_auto.go | 6 +- .../AddAudioEncoderConfiguration_auto.go | 6 +- sdk/media/AddAudioOutputConfiguration_auto.go | 6 +- sdk/media/AddAudioSourceConfiguration_auto.go | 6 +- sdk/media/AddMetadataConfiguration_auto.go | 6 +- sdk/media/AddPTZConfiguration_auto.go | 6 +- .../AddVideoAnalyticsConfiguration_auto.go | 6 +- .../AddVideoEncoderConfiguration_auto.go | 6 +- sdk/media/AddVideoSourceConfiguration_auto.go | 6 +- sdk/media/CreateOSD_auto.go | 6 +- sdk/media/CreateProfile_auto.go | 6 +- sdk/media/DeleteOSD_auto.go | 6 +- sdk/media/DeleteProfile_auto.go | 6 +- ...etAudioDecoderConfigurationOptions_auto.go | 6 +- .../GetAudioDecoderConfiguration_auto.go | 6 +- .../GetAudioDecoderConfigurations_auto.go | 6 +- ...etAudioEncoderConfigurationOptions_auto.go | 6 +- .../GetAudioEncoderConfiguration_auto.go | 6 +- .../GetAudioEncoderConfigurations_auto.go | 6 +- ...GetAudioOutputConfigurationOptions_auto.go | 6 +- sdk/media/GetAudioOutputConfiguration_auto.go | 6 +- .../GetAudioOutputConfigurations_auto.go | 6 +- sdk/media/GetAudioOutputs_auto.go | 6 +- ...GetAudioSourceConfigurationOptions_auto.go | 6 +- sdk/media/GetAudioSourceConfiguration_auto.go | 6 +- .../GetAudioSourceConfigurations_auto.go | 6 +- sdk/media/GetAudioSources_auto.go | 6 +- ...mpatibleAudioDecoderConfigurations_auto.go | 6 +- ...mpatibleAudioEncoderConfigurations_auto.go | 6 +- ...ompatibleAudioOutputConfigurations_auto.go | 6 +- ...ompatibleAudioSourceConfigurations_auto.go | 6 +- ...etCompatibleMetadataConfigurations_auto.go | 6 +- ...atibleVideoAnalyticsConfigurations_auto.go | 6 +- ...mpatibleVideoEncoderConfigurations_auto.go | 6 +- ...ompatibleVideoSourceConfigurations_auto.go | 6 +- ...nteedNumberOfVideoEncoderInstances_auto.go | 6 +- .../GetMetadataConfigurationOptions_auto.go | 6 +- sdk/media/GetMetadataConfiguration_auto.go | 6 +- sdk/media/GetMetadataConfigurations_auto.go | 6 +- sdk/media/GetOSDOptions_auto.go | 6 +- sdk/media/GetOSD_auto.go | 6 +- sdk/media/GetOSDs_auto.go | 6 +- sdk/media/GetProfile_auto.go | 6 +- sdk/media/GetProfiles_auto.go | 6 +- sdk/media/GetServiceCapabilities_auto.go | 6 +- sdk/media/GetSnapshotUri_auto.go | 6 +- sdk/media/GetStreamUri_auto.go | 6 +- .../GetVideoAnalyticsConfiguration_auto.go | 6 +- .../GetVideoAnalyticsConfigurations_auto.go | 6 +- ...etVideoEncoderConfigurationOptions_auto.go | 6 +- .../GetVideoEncoderConfiguration_auto.go | 6 +- .../GetVideoEncoderConfigurations_auto.go | 6 +- ...GetVideoSourceConfigurationOptions_auto.go | 6 +- sdk/media/GetVideoSourceConfiguration_auto.go | 6 +- .../GetVideoSourceConfigurations_auto.go | 6 +- sdk/media/GetVideoSourceModes_auto.go | 6 +- sdk/media/GetVideoSources_auto.go | 6 +- .../RemoveAudioDecoderConfiguration_auto.go | 6 +- .../RemoveAudioEncoderConfiguration_auto.go | 6 +- .../RemoveAudioOutputConfiguration_auto.go | 6 +- .../RemoveAudioSourceConfiguration_auto.go | 6 +- sdk/media/RemoveMetadataConfiguration_auto.go | 6 +- sdk/media/RemovePTZConfiguration_auto.go | 6 +- .../RemoveVideoAnalyticsConfiguration_auto.go | 6 +- .../RemoveVideoEncoderConfiguration_auto.go | 6 +- .../RemoveVideoSourceConfiguration_auto.go | 6 +- .../SetAudioDecoderConfiguration_auto.go | 6 +- .../SetAudioEncoderConfiguration_auto.go | 6 +- sdk/media/SetAudioOutputConfiguration_auto.go | 6 +- sdk/media/SetAudioSourceConfiguration_auto.go | 6 +- sdk/media/SetMetadataConfiguration_auto.go | 6 +- sdk/media/SetOSD_auto.go | 6 +- sdk/media/SetSynchronizationPoint_auto.go | 6 +- .../SetVideoAnalyticsConfiguration_auto.go | 6 +- .../SetVideoEncoderConfiguration_auto.go | 6 +- sdk/media/SetVideoSourceConfiguration_auto.go | 6 +- sdk/media/SetVideoSourceMode_auto.go | 6 +- sdk/media/StartMulticastStreaming_auto.go | 6 +- sdk/media/StopMulticastStreaming_auto.go | 6 +- sdk/media/media.go | 158 ++++++++-------- sdk/ptz/AbsoluteMove_auto.go | 6 +- sdk/ptz/ContinuousMove_auto.go | 6 +- sdk/ptz/CreatePresetTour_auto.go | 6 +- sdk/ptz/GeoMove_auto.go | 6 +- sdk/ptz/GetCompatibleConfigurations_auto.go | 6 +- sdk/ptz/GetConfigurationOptions_auto.go | 6 +- sdk/ptz/GetConfiguration_auto.go | 6 +- sdk/ptz/GetConfigurations_auto.go | 6 +- sdk/ptz/GetNode_auto.go | 6 +- sdk/ptz/GetNodes_auto.go | 6 +- sdk/ptz/GetPresetTourOptions_auto.go | 6 +- sdk/ptz/GetPresetTour_auto.go | 6 +- sdk/ptz/GetPresetTours_auto.go | 6 +- sdk/ptz/GetPresets_auto.go | 6 +- sdk/ptz/GetServiceCapabilities_auto.go | 6 +- sdk/ptz/GetStatus_auto.go | 6 +- sdk/ptz/GotoHomePosition_auto.go | 6 +- sdk/ptz/GotoPreset_auto.go | 6 +- sdk/ptz/ModifyPresetTour_auto.go | 6 +- sdk/ptz/OperatePresetTour_auto.go | 6 +- sdk/ptz/RelativeMove_auto.go | 6 +- sdk/ptz/RemovePresetTour_auto.go | 6 +- sdk/ptz/RemovePreset_auto.go | 6 +- sdk/ptz/SendAuxiliaryCommand_auto.go | 6 +- sdk/ptz/SetConfiguration_auto.go | 6 +- sdk/ptz/SetHomePosition_auto.go | 6 +- sdk/ptz/SetPreset_auto.go | 6 +- sdk/ptz/Stop_auto.go | 6 +- sdk/ptz/ptz.go | 56 +++--- 207 files changed, 830 insertions(+), 832 deletions(-) diff --git a/Device.go b/Device.go index 1801ab7..d88f548 100644 --- a/Device.go +++ b/Device.go @@ -17,7 +17,7 @@ import ( wsdiscovery "github.com/kerberos-io/onvif/ws-discovery" ) -//Xlmns XML Scheam +// Xlmns XML Scheam var Xlmns = map[string]string{ "onvif": "http://www.onvif.org/ver10/schema", "tds": "http://www.onvif.org/ver10/device/wsdl", @@ -36,7 +36,7 @@ var Xlmns = map[string]string{ "wsaw": "http://www.w3.org/2006/05/addressing/wsdl", } -//DeviceType alias for int +// DeviceType alias for int type DeviceType int // Onvif Device Tyoe @@ -63,7 +63,7 @@ func (devType DeviceType) String() string { } } -//DeviceInfo struct contains general information about ONVIF device +// DeviceInfo struct contains general information about ONVIF device type DeviceInfo struct { Manufacturer string Model string @@ -72,9 +72,9 @@ type DeviceInfo struct { HardwareId string } -//Device for a new device of onvif and DeviceInfo -//struct represents an abstract ONVIF device. -//It contains methods, which helps to communicate with ONVIF device +// Device for a new device of onvif and DeviceInfo +// struct represents an abstract ONVIF device. +// It contains methods, which helps to communicate with ONVIF device type Device struct { params DeviceParams endpoints map[string]string @@ -88,12 +88,12 @@ type DeviceParams struct { HttpClient *http.Client } -//GetServices return available endpoints +// GetServices return available endpoints func (dev *Device) GetServices() map[string]string { return dev.endpoints } -//GetServices return available endpoints +// GetServices return available endpoints func (dev *Device) GetDeviceInfo() DeviceInfo { return dev.info } @@ -106,7 +106,7 @@ func readResponse(resp *http.Response) string { return string(b) } -//GetAvailableDevicesAtSpecificEthernetInterface ... +// GetAvailableDevicesAtSpecificEthernetInterface ... func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) ([]Device, error) { // Call a ws-discovery Probe Message to Discover NVT type Devices devices, err := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) @@ -140,16 +140,8 @@ func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) ([]Dev return nvtDevices, nil } -func (dev *Device) getSupportedServices(resp *http.Response) error { +func (dev *Device) getSupportedServices(data []byte) error { doc := etree.NewDocument() - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - resp.Body.Close() - if err := doc.ReadFromBytes(data); err != nil { //log.Println(err.Error()) return err @@ -168,7 +160,7 @@ func (dev *Device) getSupportedServices(resp *http.Response) error { return nil } -//NewDevice function construct a ONVIF Device entity +// NewDevice function construct a ONVIF Device entity func NewDevice(params DeviceParams) (*Device, error) { dev := new(Device) dev.params = params @@ -183,11 +175,17 @@ func NewDevice(params DeviceParams) (*Device, error) { resp, err := dev.CallMethod(getCapabilities) + var b []byte + if resp != nil { + b, err = ioutil.ReadAll(resp.Body) + resp.Body.Close() + } + if err != nil || resp.StatusCode != http.StatusOK { return nil, errors.New("camera is not available at " + dev.params.Xaddr + " or it does not support ONVIF services") } - err = dev.getSupportedServices(resp) + err = dev.getSupportedServices(b) if err != nil { return nil, err } @@ -209,7 +207,7 @@ func (dev *Device) addEndpoint(Key, Value string) { dev.endpoints[lowCaseKey] = Value } -//GetEndpoint returns specific ONVIF service endpoint address +// GetEndpoint returns specific ONVIF service endpoint address func (dev *Device) GetEndpoint(name string) string { return dev.endpoints[name] } @@ -229,7 +227,7 @@ func (dev Device) buildMethodSOAP(msg string) (gosoap.SoapMessage, error) { return soap, nil } -//getEndpoint functions get the target service endpoint in a better way +// getEndpoint functions get the target service endpoint in a better way func (dev Device) getEndpoint(endpoint string) (string, error) { // common condition, endpointMark in map we use this. @@ -250,8 +248,8 @@ func (dev Device) getEndpoint(endpoint string) (string, error) { return endpointURL, errors.New("target endpoint service not found") } -//CallMethod functions call an method, defined struct. -//You should use Authenticate method to call authorized requests. +// CallMethod functions call an method, defined struct. +// You should use Authenticate method to call authorized requests. func (dev Device) CallMethod(method interface{}) (*http.Response, error) { pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/") pkg := strings.ToLower(pkgPath[len(pkgPath)-1]) @@ -263,7 +261,7 @@ func (dev Device) CallMethod(method interface{}) (*http.Response, error) { return dev.callMethodDo(endpoint, method) } -//CallMethod functions call an method, defined struct with authentication data +// CallMethod functions call an method, defined struct with authentication data func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) { output, err := xml.MarshalIndent(method, " ", " ") if err != nil { diff --git a/examples/DeviceService.go b/examples/DeviceService.go index 84cc9bd..5bf6aa8 100644 --- a/examples/DeviceService.go +++ b/examples/DeviceService.go @@ -6,10 +6,10 @@ import ( "log" "net/http" - goonvif "github.com/use-go/onvif" - "github.com/use-go/onvif/device" - sdk "github.com/use-go/onvif/sdk/device" - "github.com/use-go/onvif/xsd/onvif" + goonvif "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/device" + sdk "github.com/kerberos-io/onvif/sdk/device" + "github.com/kerberos-io/onvif/xsd/onvif" ) const ( @@ -60,7 +60,7 @@ func main() { if err != nil { log.Println(err) } else { - // You could use https://github.com/use-go/onvif/gosoap for pretty printing response + // You could use https://github.com/kerberos-io/onvif/gosoap for pretty printing response fmt.Println(createUserResponse) } diff --git a/sdk/codegen/main.go b/sdk/codegen/main.go index d49b0b6..9884c99 100644 --- a/sdk/codegen/main.go +++ b/sdk/codegen/main.go @@ -19,9 +19,9 @@ package {{.Package}} import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/{{.StructPackage}}" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/{{.StructPackage}}" ) // Call_{{.TypeRequest}} forwards the call to dev.CallMethod() then parses the payload of the reply as a {{.TypeReply}}. diff --git a/sdk/device/AddIPAddressFilter_auto.go b/sdk/device/AddIPAddressFilter_auto.go index bb74bb2..15cd4df 100644 --- a/sdk/device/AddIPAddressFilter_auto.go +++ b/sdk/device/AddIPAddressFilter_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_AddIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a AddIPAddressFilterResponse. diff --git a/sdk/device/AddScopes_auto.go b/sdk/device/AddScopes_auto.go index d665d94..0f765f4 100644 --- a/sdk/device/AddScopes_auto.go +++ b/sdk/device/AddScopes_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_AddScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a AddScopesResponse. diff --git a/sdk/device/CreateCertificate_auto.go b/sdk/device/CreateCertificate_auto.go index 3409679..1a733d5 100644 --- a/sdk/device/CreateCertificate_auto.go +++ b/sdk/device/CreateCertificate_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_CreateCertificate forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateCertificateResponse. diff --git a/sdk/device/CreateDot1XConfiguration_auto.go b/sdk/device/CreateDot1XConfiguration_auto.go index 43de94c..c34ec64 100644 --- a/sdk/device/CreateDot1XConfiguration_auto.go +++ b/sdk/device/CreateDot1XConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_CreateDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateDot1XConfigurationResponse. diff --git a/sdk/device/CreateStorageConfiguration_auto.go b/sdk/device/CreateStorageConfiguration_auto.go index 78f27ff..542f374 100644 --- a/sdk/device/CreateStorageConfiguration_auto.go +++ b/sdk/device/CreateStorageConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_CreateStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateStorageConfigurationResponse. diff --git a/sdk/device/CreateUsers_auto.go b/sdk/device/CreateUsers_auto.go index 4eabb15..a938bed 100644 --- a/sdk/device/CreateUsers_auto.go +++ b/sdk/device/CreateUsers_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_CreateUsers forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateUsersResponse. diff --git a/sdk/device/DeleteCertificates_auto.go b/sdk/device/DeleteCertificates_auto.go index 15390bf..5aedbe8 100644 --- a/sdk/device/DeleteCertificates_auto.go +++ b/sdk/device/DeleteCertificates_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_DeleteCertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteCertificatesResponse. diff --git a/sdk/device/DeleteDot1XConfiguration_auto.go b/sdk/device/DeleteDot1XConfiguration_auto.go index 25335b2..51dea1f 100644 --- a/sdk/device/DeleteDot1XConfiguration_auto.go +++ b/sdk/device/DeleteDot1XConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_DeleteDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteDot1XConfigurationResponse. diff --git a/sdk/device/DeleteGeoLocation_auto.go b/sdk/device/DeleteGeoLocation_auto.go index 7b1be28..13a2af7 100644 --- a/sdk/device/DeleteGeoLocation_auto.go +++ b/sdk/device/DeleteGeoLocation_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_DeleteGeoLocation forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteGeoLocationResponse. diff --git a/sdk/device/DeleteStorageConfiguration_auto.go b/sdk/device/DeleteStorageConfiguration_auto.go index 8362441..7866d53 100644 --- a/sdk/device/DeleteStorageConfiguration_auto.go +++ b/sdk/device/DeleteStorageConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_DeleteStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteStorageConfigurationResponse. diff --git a/sdk/device/DeleteUsers_auto.go b/sdk/device/DeleteUsers_auto.go index 4c00337..786a5a4 100644 --- a/sdk/device/DeleteUsers_auto.go +++ b/sdk/device/DeleteUsers_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_DeleteUsers forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteUsersResponse. diff --git a/sdk/device/GetAccessPolicy_auto.go b/sdk/device/GetAccessPolicy_auto.go index 4710b82..66ac1ee 100644 --- a/sdk/device/GetAccessPolicy_auto.go +++ b/sdk/device/GetAccessPolicy_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetAccessPolicy forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAccessPolicyResponse. diff --git a/sdk/device/GetCACertificates_auto.go b/sdk/device/GetCACertificates_auto.go index 8fb1eaf..160a8c2 100644 --- a/sdk/device/GetCACertificates_auto.go +++ b/sdk/device/GetCACertificates_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetCACertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCACertificatesResponse. diff --git a/sdk/device/GetCapabilities_auto.go b/sdk/device/GetCapabilities_auto.go index 86fc154..8df95ee 100644 --- a/sdk/device/GetCapabilities_auto.go +++ b/sdk/device/GetCapabilities_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCapabilitiesResponse. diff --git a/sdk/device/GetCertificateInformation_auto.go b/sdk/device/GetCertificateInformation_auto.go index 4d966d4..f91541b 100644 --- a/sdk/device/GetCertificateInformation_auto.go +++ b/sdk/device/GetCertificateInformation_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetCertificateInformation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCertificateInformationResponse. diff --git a/sdk/device/GetCertificatesStatus_auto.go b/sdk/device/GetCertificatesStatus_auto.go index 6f3da11..1981b6b 100644 --- a/sdk/device/GetCertificatesStatus_auto.go +++ b/sdk/device/GetCertificatesStatus_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetCertificatesStatus forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCertificatesStatusResponse. diff --git a/sdk/device/GetCertificates_auto.go b/sdk/device/GetCertificates_auto.go index ec856fb..7143c7d 100644 --- a/sdk/device/GetCertificates_auto.go +++ b/sdk/device/GetCertificates_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetCertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCertificatesResponse. diff --git a/sdk/device/GetClientCertificateMode_auto.go b/sdk/device/GetClientCertificateMode_auto.go index 122be49..9f3ba10 100644 --- a/sdk/device/GetClientCertificateMode_auto.go +++ b/sdk/device/GetClientCertificateMode_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetClientCertificateMode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetClientCertificateModeResponse. diff --git a/sdk/device/GetDNS_auto.go b/sdk/device/GetDNS_auto.go index 3fe0f84..68f60d2 100644 --- a/sdk/device/GetDNS_auto.go +++ b/sdk/device/GetDNS_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDNSResponse. diff --git a/sdk/device/GetDPAddresses_auto.go b/sdk/device/GetDPAddresses_auto.go index e8e8b80..6b12e4d 100644 --- a/sdk/device/GetDPAddresses_auto.go +++ b/sdk/device/GetDPAddresses_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDPAddresses forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDPAddressesResponse. diff --git a/sdk/device/GetDeviceInformation_auto.go b/sdk/device/GetDeviceInformation_auto.go index dac510d..9511f7e 100644 --- a/sdk/device/GetDeviceInformation_auto.go +++ b/sdk/device/GetDeviceInformation_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDeviceInformation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDeviceInformationResponse. diff --git a/sdk/device/GetDiscoveryMode_auto.go b/sdk/device/GetDiscoveryMode_auto.go index 5d8565e..81c8b2b 100644 --- a/sdk/device/GetDiscoveryMode_auto.go +++ b/sdk/device/GetDiscoveryMode_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDiscoveryModeResponse. diff --git a/sdk/device/GetDot11Capabilities_auto.go b/sdk/device/GetDot11Capabilities_auto.go index f9cb0b8..68cfc0f 100644 --- a/sdk/device/GetDot11Capabilities_auto.go +++ b/sdk/device/GetDot11Capabilities_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDot11Capabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot11CapabilitiesResponse. diff --git a/sdk/device/GetDot11Status_auto.go b/sdk/device/GetDot11Status_auto.go index 8b92b70..7256878 100644 --- a/sdk/device/GetDot11Status_auto.go +++ b/sdk/device/GetDot11Status_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDot11Status forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot11StatusResponse. diff --git a/sdk/device/GetDot1XConfiguration_auto.go b/sdk/device/GetDot1XConfiguration_auto.go index 3e6531f..19e3e64 100644 --- a/sdk/device/GetDot1XConfiguration_auto.go +++ b/sdk/device/GetDot1XConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot1XConfigurationResponse. diff --git a/sdk/device/GetDot1XConfigurations_auto.go b/sdk/device/GetDot1XConfigurations_auto.go index 5b3c8a1..3b49779 100644 --- a/sdk/device/GetDot1XConfigurations_auto.go +++ b/sdk/device/GetDot1XConfigurations_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDot1XConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot1XConfigurationsResponse. diff --git a/sdk/device/GetDynamicDNS_auto.go b/sdk/device/GetDynamicDNS_auto.go index aba75d4..7cf603b 100644 --- a/sdk/device/GetDynamicDNS_auto.go +++ b/sdk/device/GetDynamicDNS_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetDynamicDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDynamicDNSResponse. diff --git a/sdk/device/GetEndpointReference_auto.go b/sdk/device/GetEndpointReference_auto.go index ac40ae4..2261c52 100644 --- a/sdk/device/GetEndpointReference_auto.go +++ b/sdk/device/GetEndpointReference_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetEndpointReference forwards the call to dev.CallMethod() then parses the payload of the reply as a GetEndpointReferenceResponse. diff --git a/sdk/device/GetGeoLocation_auto.go b/sdk/device/GetGeoLocation_auto.go index 20febac..c734c7f 100644 --- a/sdk/device/GetGeoLocation_auto.go +++ b/sdk/device/GetGeoLocation_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetGeoLocation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetGeoLocationResponse. diff --git a/sdk/device/GetHostname_auto.go b/sdk/device/GetHostname_auto.go index 8cacaa7..79c67e6 100644 --- a/sdk/device/GetHostname_auto.go +++ b/sdk/device/GetHostname_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetHostname forwards the call to dev.CallMethod() then parses the payload of the reply as a GetHostnameResponse. diff --git a/sdk/device/GetIPAddressFilter_auto.go b/sdk/device/GetIPAddressFilter_auto.go index 1944bc2..9c7fd95 100644 --- a/sdk/device/GetIPAddressFilter_auto.go +++ b/sdk/device/GetIPAddressFilter_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a GetIPAddressFilterResponse. diff --git a/sdk/device/GetNTP_auto.go b/sdk/device/GetNTP_auto.go index 45b8f5a..8851044 100644 --- a/sdk/device/GetNTP_auto.go +++ b/sdk/device/GetNTP_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetNTP forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNTPResponse. diff --git a/sdk/device/GetNetworkDefaultGateway_auto.go b/sdk/device/GetNetworkDefaultGateway_auto.go index 6b38097..3f2dff4 100644 --- a/sdk/device/GetNetworkDefaultGateway_auto.go +++ b/sdk/device/GetNetworkDefaultGateway_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetNetworkDefaultGateway forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNetworkDefaultGatewayResponse. diff --git a/sdk/device/GetNetworkInterfaces_auto.go b/sdk/device/GetNetworkInterfaces_auto.go index 3b37ca7..a19be90 100644 --- a/sdk/device/GetNetworkInterfaces_auto.go +++ b/sdk/device/GetNetworkInterfaces_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetNetworkInterfaces forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNetworkInterfacesResponse. diff --git a/sdk/device/GetNetworkProtocols_auto.go b/sdk/device/GetNetworkProtocols_auto.go index 0fe53f3..b922d68 100644 --- a/sdk/device/GetNetworkProtocols_auto.go +++ b/sdk/device/GetNetworkProtocols_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetNetworkProtocols forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNetworkProtocolsResponse. diff --git a/sdk/device/GetPkcs10Request_auto.go b/sdk/device/GetPkcs10Request_auto.go index b5e7861..d4cc7e8 100644 --- a/sdk/device/GetPkcs10Request_auto.go +++ b/sdk/device/GetPkcs10Request_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetPkcs10Request forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPkcs10RequestResponse. diff --git a/sdk/device/GetRelayOutputs_auto.go b/sdk/device/GetRelayOutputs_auto.go index fedd4fe..732ab49 100644 --- a/sdk/device/GetRelayOutputs_auto.go +++ b/sdk/device/GetRelayOutputs_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetRelayOutputs forwards the call to dev.CallMethod() then parses the payload of the reply as a GetRelayOutputsResponse. diff --git a/sdk/device/GetRemoteDiscoveryMode_auto.go b/sdk/device/GetRemoteDiscoveryMode_auto.go index b8d36bb..263fa23 100644 --- a/sdk/device/GetRemoteDiscoveryMode_auto.go +++ b/sdk/device/GetRemoteDiscoveryMode_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetRemoteDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetRemoteDiscoveryModeResponse. diff --git a/sdk/device/GetRemoteUser_auto.go b/sdk/device/GetRemoteUser_auto.go index cc32bd6..bdab0b1 100644 --- a/sdk/device/GetRemoteUser_auto.go +++ b/sdk/device/GetRemoteUser_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetRemoteUser forwards the call to dev.CallMethod() then parses the payload of the reply as a GetRemoteUserResponse. diff --git a/sdk/device/GetScopes_auto.go b/sdk/device/GetScopes_auto.go index c23bf58..9f68ecb 100644 --- a/sdk/device/GetScopes_auto.go +++ b/sdk/device/GetScopes_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a GetScopesResponse. diff --git a/sdk/device/GetServiceCapabilities_auto.go b/sdk/device/GetServiceCapabilities_auto.go index bdbc390..54f970c 100644 --- a/sdk/device/GetServiceCapabilities_auto.go +++ b/sdk/device/GetServiceCapabilities_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. diff --git a/sdk/device/GetServices_auto.go b/sdk/device/GetServices_auto.go index 15d3593..bfbef44 100644 --- a/sdk/device/GetServices_auto.go +++ b/sdk/device/GetServices_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetServices forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServicesResponse. diff --git a/sdk/device/GetStorageConfiguration_auto.go b/sdk/device/GetStorageConfiguration_auto.go index 1bdf670..ceac17c 100644 --- a/sdk/device/GetStorageConfiguration_auto.go +++ b/sdk/device/GetStorageConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStorageConfigurationResponse. diff --git a/sdk/device/GetStorageConfigurations_auto.go b/sdk/device/GetStorageConfigurations_auto.go index bb83e06..502be95 100644 --- a/sdk/device/GetStorageConfigurations_auto.go +++ b/sdk/device/GetStorageConfigurations_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetStorageConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStorageConfigurationsResponse. diff --git a/sdk/device/GetSystemBackup_auto.go b/sdk/device/GetSystemBackup_auto.go index 773f509..37884d0 100644 --- a/sdk/device/GetSystemBackup_auto.go +++ b/sdk/device/GetSystemBackup_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetSystemBackup forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemBackupResponse. diff --git a/sdk/device/GetSystemDateAndTime_auto.go b/sdk/device/GetSystemDateAndTime_auto.go index 4f54ea9..6c2d265 100644 --- a/sdk/device/GetSystemDateAndTime_auto.go +++ b/sdk/device/GetSystemDateAndTime_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetSystemDateAndTime forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemDateAndTimeResponse. diff --git a/sdk/device/GetSystemLog_auto.go b/sdk/device/GetSystemLog_auto.go index 40c969a..87b9945 100644 --- a/sdk/device/GetSystemLog_auto.go +++ b/sdk/device/GetSystemLog_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetSystemLog forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemLogResponse. diff --git a/sdk/device/GetSystemSupportInformation_auto.go b/sdk/device/GetSystemSupportInformation_auto.go index 9a9a396..16593ad 100644 --- a/sdk/device/GetSystemSupportInformation_auto.go +++ b/sdk/device/GetSystemSupportInformation_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetSystemSupportInformation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemSupportInformationResponse. diff --git a/sdk/device/GetSystemUris_auto.go b/sdk/device/GetSystemUris_auto.go index 4a7d2af..1eec175 100644 --- a/sdk/device/GetSystemUris_auto.go +++ b/sdk/device/GetSystemUris_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetSystemUris forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemUrisResponse. diff --git a/sdk/device/GetUsers_auto.go b/sdk/device/GetUsers_auto.go index 156d7c9..95d863d 100644 --- a/sdk/device/GetUsers_auto.go +++ b/sdk/device/GetUsers_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetUsers forwards the call to dev.CallMethod() then parses the payload of the reply as a GetUsersResponse. diff --git a/sdk/device/GetWsdlUrl_auto.go b/sdk/device/GetWsdlUrl_auto.go index 5f49e10..a010bcc 100644 --- a/sdk/device/GetWsdlUrl_auto.go +++ b/sdk/device/GetWsdlUrl_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetWsdlUrl forwards the call to dev.CallMethod() then parses the payload of the reply as a GetWsdlUrlResponse. diff --git a/sdk/device/GetZeroConfiguration_auto.go b/sdk/device/GetZeroConfiguration_auto.go index 3e01154..02887a8 100644 --- a/sdk/device/GetZeroConfiguration_auto.go +++ b/sdk/device/GetZeroConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_GetZeroConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetZeroConfigurationResponse. diff --git a/sdk/device/LoadCACertificates_auto.go b/sdk/device/LoadCACertificates_auto.go index 6c5aacd..8a15da6 100644 --- a/sdk/device/LoadCACertificates_auto.go +++ b/sdk/device/LoadCACertificates_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_LoadCACertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a LoadCACertificatesResponse. diff --git a/sdk/device/LoadCertificateWithPrivateKey_auto.go b/sdk/device/LoadCertificateWithPrivateKey_auto.go index 3184f84..376952b 100644 --- a/sdk/device/LoadCertificateWithPrivateKey_auto.go +++ b/sdk/device/LoadCertificateWithPrivateKey_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_LoadCertificateWithPrivateKey forwards the call to dev.CallMethod() then parses the payload of the reply as a LoadCertificateWithPrivateKeyResponse. diff --git a/sdk/device/LoadCertificates_auto.go b/sdk/device/LoadCertificates_auto.go index 04eb82b..7a2385a 100644 --- a/sdk/device/LoadCertificates_auto.go +++ b/sdk/device/LoadCertificates_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_LoadCertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a LoadCertificatesResponse. diff --git a/sdk/device/RemoveIPAddressFilter_auto.go b/sdk/device/RemoveIPAddressFilter_auto.go index 072f337..fabb4c6 100644 --- a/sdk/device/RemoveIPAddressFilter_auto.go +++ b/sdk/device/RemoveIPAddressFilter_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_RemoveIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveIPAddressFilterResponse. diff --git a/sdk/device/RemoveScopes_auto.go b/sdk/device/RemoveScopes_auto.go index ffcd995..3aa699f 100644 --- a/sdk/device/RemoveScopes_auto.go +++ b/sdk/device/RemoveScopes_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_RemoveScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveScopesResponse. diff --git a/sdk/device/RestoreSystem_auto.go b/sdk/device/RestoreSystem_auto.go index 00515a0..4a18cc6 100644 --- a/sdk/device/RestoreSystem_auto.go +++ b/sdk/device/RestoreSystem_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_RestoreSystem forwards the call to dev.CallMethod() then parses the payload of the reply as a RestoreSystemResponse. diff --git a/sdk/device/ScanAvailableDot11Networks_auto.go b/sdk/device/ScanAvailableDot11Networks_auto.go index 7df81bc..ba8a498 100644 --- a/sdk/device/ScanAvailableDot11Networks_auto.go +++ b/sdk/device/ScanAvailableDot11Networks_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_ScanAvailableDot11Networks forwards the call to dev.CallMethod() then parses the payload of the reply as a ScanAvailableDot11NetworksResponse. diff --git a/sdk/device/SendAuxiliaryCommand_auto.go b/sdk/device/SendAuxiliaryCommand_auto.go index 2d24019..df23a07 100644 --- a/sdk/device/SendAuxiliaryCommand_auto.go +++ b/sdk/device/SendAuxiliaryCommand_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SendAuxiliaryCommand forwards the call to dev.CallMethod() then parses the payload of the reply as a SendAuxiliaryCommandResponse. diff --git a/sdk/device/SetAccessPolicy_auto.go b/sdk/device/SetAccessPolicy_auto.go index 922173e..4c29b29 100644 --- a/sdk/device/SetAccessPolicy_auto.go +++ b/sdk/device/SetAccessPolicy_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetAccessPolicy forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAccessPolicyResponse. diff --git a/sdk/device/SetCertificatesStatus_auto.go b/sdk/device/SetCertificatesStatus_auto.go index 09be5ac..c3ff486 100644 --- a/sdk/device/SetCertificatesStatus_auto.go +++ b/sdk/device/SetCertificatesStatus_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetCertificatesStatus forwards the call to dev.CallMethod() then parses the payload of the reply as a SetCertificatesStatusResponse. diff --git a/sdk/device/SetClientCertificateMode_auto.go b/sdk/device/SetClientCertificateMode_auto.go index 50fd216..3eae86f 100644 --- a/sdk/device/SetClientCertificateMode_auto.go +++ b/sdk/device/SetClientCertificateMode_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetClientCertificateMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetClientCertificateModeResponse. diff --git a/sdk/device/SetDNS_auto.go b/sdk/device/SetDNS_auto.go index b412166..111f885 100644 --- a/sdk/device/SetDNS_auto.go +++ b/sdk/device/SetDNS_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDNSResponse. diff --git a/sdk/device/SetDiscoveryMode_auto.go b/sdk/device/SetDiscoveryMode_auto.go index e5628ef..605fcab 100644 --- a/sdk/device/SetDiscoveryMode_auto.go +++ b/sdk/device/SetDiscoveryMode_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDiscoveryModeResponse. diff --git a/sdk/device/SetDot1XConfiguration_auto.go b/sdk/device/SetDot1XConfiguration_auto.go index 02fff39..9c54e53 100644 --- a/sdk/device/SetDot1XConfiguration_auto.go +++ b/sdk/device/SetDot1XConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDot1XConfigurationResponse. diff --git a/sdk/device/SetDynamicDNS_auto.go b/sdk/device/SetDynamicDNS_auto.go index 9bf5424..8eb6ee0 100644 --- a/sdk/device/SetDynamicDNS_auto.go +++ b/sdk/device/SetDynamicDNS_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetDynamicDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDynamicDNSResponse. diff --git a/sdk/device/SetGeoLocation_auto.go b/sdk/device/SetGeoLocation_auto.go index 165f0f8..0e48399 100644 --- a/sdk/device/SetGeoLocation_auto.go +++ b/sdk/device/SetGeoLocation_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetGeoLocation forwards the call to dev.CallMethod() then parses the payload of the reply as a SetGeoLocationResponse. diff --git a/sdk/device/SetHostnameFromDHCP_auto.go b/sdk/device/SetHostnameFromDHCP_auto.go index 6c206d6..96c1af5 100644 --- a/sdk/device/SetHostnameFromDHCP_auto.go +++ b/sdk/device/SetHostnameFromDHCP_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetHostnameFromDHCP forwards the call to dev.CallMethod() then parses the payload of the reply as a SetHostnameFromDHCPResponse. diff --git a/sdk/device/SetHostname_auto.go b/sdk/device/SetHostname_auto.go index 2c16860..f7bae40 100644 --- a/sdk/device/SetHostname_auto.go +++ b/sdk/device/SetHostname_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetHostname forwards the call to dev.CallMethod() then parses the payload of the reply as a SetHostnameResponse. diff --git a/sdk/device/SetIPAddressFilter_auto.go b/sdk/device/SetIPAddressFilter_auto.go index 37203dc..6900b6c 100644 --- a/sdk/device/SetIPAddressFilter_auto.go +++ b/sdk/device/SetIPAddressFilter_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a SetIPAddressFilterResponse. diff --git a/sdk/device/SetNTP_auto.go b/sdk/device/SetNTP_auto.go index e33ff4c..92eeb0e 100644 --- a/sdk/device/SetNTP_auto.go +++ b/sdk/device/SetNTP_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetNTP forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNTPResponse. diff --git a/sdk/device/SetNetworkDefaultGateway_auto.go b/sdk/device/SetNetworkDefaultGateway_auto.go index e56cf81..8110bdd 100644 --- a/sdk/device/SetNetworkDefaultGateway_auto.go +++ b/sdk/device/SetNetworkDefaultGateway_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetNetworkDefaultGateway forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNetworkDefaultGatewayResponse. diff --git a/sdk/device/SetNetworkInterfaces_auto.go b/sdk/device/SetNetworkInterfaces_auto.go index 09bda76..8e11245 100644 --- a/sdk/device/SetNetworkInterfaces_auto.go +++ b/sdk/device/SetNetworkInterfaces_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetNetworkInterfaces forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNetworkInterfacesResponse. diff --git a/sdk/device/SetNetworkProtocols_auto.go b/sdk/device/SetNetworkProtocols_auto.go index a9cc93d..05cb82c 100644 --- a/sdk/device/SetNetworkProtocols_auto.go +++ b/sdk/device/SetNetworkProtocols_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetNetworkProtocols forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNetworkProtocolsResponse. diff --git a/sdk/device/SetRelayOutputSettings_auto.go b/sdk/device/SetRelayOutputSettings_auto.go index ded4092..5420134 100644 --- a/sdk/device/SetRelayOutputSettings_auto.go +++ b/sdk/device/SetRelayOutputSettings_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetRelayOutputSettings forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRelayOutputSettingsResponse. diff --git a/sdk/device/SetRelayOutputState_auto.go b/sdk/device/SetRelayOutputState_auto.go index 9a94cab..f946c46 100644 --- a/sdk/device/SetRelayOutputState_auto.go +++ b/sdk/device/SetRelayOutputState_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetRelayOutputState forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRelayOutputStateResponse. diff --git a/sdk/device/SetRemoteDiscoveryMode_auto.go b/sdk/device/SetRemoteDiscoveryMode_auto.go index ce8db58..53c143c 100644 --- a/sdk/device/SetRemoteDiscoveryMode_auto.go +++ b/sdk/device/SetRemoteDiscoveryMode_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetRemoteDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRemoteDiscoveryModeResponse. diff --git a/sdk/device/SetRemoteUser_auto.go b/sdk/device/SetRemoteUser_auto.go index 5754ee7..6d2454c 100644 --- a/sdk/device/SetRemoteUser_auto.go +++ b/sdk/device/SetRemoteUser_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetRemoteUser forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRemoteUserResponse. diff --git a/sdk/device/SetScopes_auto.go b/sdk/device/SetScopes_auto.go index 1acef52..e1f0195 100644 --- a/sdk/device/SetScopes_auto.go +++ b/sdk/device/SetScopes_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a SetScopesResponse. diff --git a/sdk/device/SetStorageConfiguration_auto.go b/sdk/device/SetStorageConfiguration_auto.go index 18a508b..59dfcf1 100644 --- a/sdk/device/SetStorageConfiguration_auto.go +++ b/sdk/device/SetStorageConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetStorageConfigurationResponse. diff --git a/sdk/device/SetSystemDateAndTime_auto.go b/sdk/device/SetSystemDateAndTime_auto.go index 71f2535..25f57bb 100644 --- a/sdk/device/SetSystemDateAndTime_auto.go +++ b/sdk/device/SetSystemDateAndTime_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetSystemDateAndTime forwards the call to dev.CallMethod() then parses the payload of the reply as a SetSystemDateAndTimeResponse. diff --git a/sdk/device/SetSystemFactoryDefault_auto.go b/sdk/device/SetSystemFactoryDefault_auto.go index ad15c98..9616b10 100644 --- a/sdk/device/SetSystemFactoryDefault_auto.go +++ b/sdk/device/SetSystemFactoryDefault_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetSystemFactoryDefault forwards the call to dev.CallMethod() then parses the payload of the reply as a SetSystemFactoryDefaultResponse. diff --git a/sdk/device/SetUser_auto.go b/sdk/device/SetUser_auto.go index 713aa4e..3c7f030 100644 --- a/sdk/device/SetUser_auto.go +++ b/sdk/device/SetUser_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetUser forwards the call to dev.CallMethod() then parses the payload of the reply as a SetUserResponse. diff --git a/sdk/device/SetZeroConfiguration_auto.go b/sdk/device/SetZeroConfiguration_auto.go index c4c849a..a3b76e6 100644 --- a/sdk/device/SetZeroConfiguration_auto.go +++ b/sdk/device/SetZeroConfiguration_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SetZeroConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetZeroConfigurationResponse. diff --git a/sdk/device/StartFirmwareUpgrade_auto.go b/sdk/device/StartFirmwareUpgrade_auto.go index 5f04ad9..e890ae2 100644 --- a/sdk/device/StartFirmwareUpgrade_auto.go +++ b/sdk/device/StartFirmwareUpgrade_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_StartFirmwareUpgrade forwards the call to dev.CallMethod() then parses the payload of the reply as a StartFirmwareUpgradeResponse. diff --git a/sdk/device/StartSystemRestore_auto.go b/sdk/device/StartSystemRestore_auto.go index c542e5a..ab9d35d 100644 --- a/sdk/device/StartSystemRestore_auto.go +++ b/sdk/device/StartSystemRestore_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_StartSystemRestore forwards the call to dev.CallMethod() then parses the payload of the reply as a StartSystemRestoreResponse. diff --git a/sdk/device/SystemReboot_auto.go b/sdk/device/SystemReboot_auto.go index d9879fd..6ad6831 100644 --- a/sdk/device/SystemReboot_auto.go +++ b/sdk/device/SystemReboot_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_SystemReboot forwards the call to dev.CallMethod() then parses the payload of the reply as a SystemRebootResponse. diff --git a/sdk/device/UpgradeSystemFirmware_auto.go b/sdk/device/UpgradeSystemFirmware_auto.go index 9fc3b11..a6d63af 100644 --- a/sdk/device/UpgradeSystemFirmware_auto.go +++ b/sdk/device/UpgradeSystemFirmware_auto.go @@ -7,9 +7,9 @@ package device import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/device" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/device" ) // Call_UpgradeSystemFirmware forwards the call to dev.CallMethod() then parses the payload of the reply as a UpgradeSystemFirmwareResponse. diff --git a/sdk/device/device.go b/sdk/device/device.go index e65a629..1f4c1a8 100644 --- a/sdk/device/device.go +++ b/sdk/device/device.go @@ -1,91 +1,91 @@ package device -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetServices -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetServiceCapabilities -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDeviceInformation -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetSystemDateAndTime -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetSystemDateAndTime -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetSystemFactoryDefault -//go:generate go run github.com/use-go/onvif/sdk/codegen device device UpgradeSystemFirmware -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SystemReboot -//go:generate go run github.com/use-go/onvif/sdk/codegen device device RestoreSystem -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetSystemBackup -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetSystemLog -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetSystemSupportInformation -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetScopes -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetScopes -//go:generate go run github.com/use-go/onvif/sdk/codegen device device AddScopes -//go:generate go run github.com/use-go/onvif/sdk/codegen device device RemoveScopes -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDiscoveryMode -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetDiscoveryMode -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetRemoteDiscoveryMode -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetRemoteDiscoveryMode -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDPAddresses -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetEndpointReference -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetRemoteUser -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetRemoteUser -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetUsers -//go:generate go run github.com/use-go/onvif/sdk/codegen device device CreateUsers -//go:generate go run github.com/use-go/onvif/sdk/codegen device device DeleteUsers -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetUser -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetWsdlUrl -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetCapabilities -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetHostname -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetHostname -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetHostnameFromDHCP -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDNS -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetDNS -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetNTP -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetNTP -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDynamicDNS -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetDynamicDNS -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetNetworkInterfaces -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetNetworkInterfaces -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetNetworkProtocols -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetNetworkProtocols -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetNetworkDefaultGateway -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetNetworkDefaultGateway -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetZeroConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetZeroConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetIPAddressFilter -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetIPAddressFilter -//go:generate go run github.com/use-go/onvif/sdk/codegen device device AddIPAddressFilter -//go:generate go run github.com/use-go/onvif/sdk/codegen device device RemoveIPAddressFilter -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetAccessPolicy -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetAccessPolicy -//go:generate go run github.com/use-go/onvif/sdk/codegen device device CreateCertificate -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetCertificates -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetCertificatesStatus -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetCertificatesStatus -//go:generate go run github.com/use-go/onvif/sdk/codegen device device DeleteCertificates -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetPkcs10Request -//go:generate go run github.com/use-go/onvif/sdk/codegen device device LoadCertificates -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetClientCertificateMode -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetClientCertificateMode -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetRelayOutputs -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetRelayOutputSettings -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetRelayOutputState -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SendAuxiliaryCommand -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetCACertificates -//go:generate go run github.com/use-go/onvif/sdk/codegen device device LoadCertificateWithPrivateKey -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetCertificateInformation -//go:generate go run github.com/use-go/onvif/sdk/codegen device device LoadCACertificates -//go:generate go run github.com/use-go/onvif/sdk/codegen device device CreateDot1XConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetDot1XConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDot1XConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDot1XConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen device device DeleteDot1XConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDot11Capabilities -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetDot11Status -//go:generate go run github.com/use-go/onvif/sdk/codegen device device ScanAvailableDot11Networks -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetSystemUris -//go:generate go run github.com/use-go/onvif/sdk/codegen device device StartFirmwareUpgrade -//go:generate go run github.com/use-go/onvif/sdk/codegen device device StartSystemRestore -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetStorageConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen device device CreateStorageConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetStorageConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetStorageConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device DeleteStorageConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen device device GetGeoLocation -//go:generate go run github.com/use-go/onvif/sdk/codegen device device SetGeoLocation -//go:generate go run github.com/use-go/onvif/sdk/codegen device device DeleteGeoLocation +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetServices +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetServiceCapabilities +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDeviceInformation +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetSystemDateAndTime +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemDateAndTime +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetSystemFactoryDefault +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device UpgradeSystemFirmware +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SystemReboot +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device RestoreSystem +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemBackup +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemLog +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemSupportInformation +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetScopes +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetScopes +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device AddScopes +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device RemoveScopes +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDiscoveryMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDiscoveryMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetRemoteDiscoveryMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRemoteDiscoveryMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDPAddresses +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetEndpointReference +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetRemoteUser +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRemoteUser +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetUsers +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateUsers +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteUsers +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetUser +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetWsdlUrl +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCapabilities +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetHostname +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetHostname +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetHostnameFromDHCP +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDNS +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDNS +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNTP +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNTP +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDynamicDNS +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDynamicDNS +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNetworkInterfaces +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNetworkInterfaces +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNetworkProtocols +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNetworkProtocols +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNetworkDefaultGateway +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNetworkDefaultGateway +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetZeroConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetZeroConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetIPAddressFilter +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetIPAddressFilter +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device AddIPAddressFilter +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device RemoveIPAddressFilter +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetAccessPolicy +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetAccessPolicy +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateCertificate +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCertificates +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCertificatesStatus +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetCertificatesStatus +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteCertificates +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetPkcs10Request +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device LoadCertificates +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetClientCertificateMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetClientCertificateMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetRelayOutputs +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRelayOutputSettings +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRelayOutputState +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SendAuxiliaryCommand +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCACertificates +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device LoadCertificateWithPrivateKey +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCertificateInformation +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device LoadCACertificates +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateDot1XConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDot1XConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot1XConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot1XConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteDot1XConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot11Capabilities +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot11Status +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device ScanAvailableDot11Networks +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemUris +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device StartFirmwareUpgrade +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device StartSystemRestore +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetStorageConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateStorageConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetStorageConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetStorageConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteStorageConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetGeoLocation +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetGeoLocation +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteGeoLocation diff --git a/sdk/event/CreatePullPointSubscription_auto.go b/sdk/event/CreatePullPointSubscription_auto.go index b4b70ab..971f31b 100644 --- a/sdk/event/CreatePullPointSubscription_auto.go +++ b/sdk/event/CreatePullPointSubscription_auto.go @@ -7,9 +7,9 @@ package event import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/event" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/event" ) // Call_CreatePullPointSubscription forwards the call to dev.CallMethod() then parses the payload of the reply as a CreatePullPointSubscriptionResponse. diff --git a/sdk/event/GetEventProperties_auto.go b/sdk/event/GetEventProperties_auto.go index 887e5bd..839ad5d 100644 --- a/sdk/event/GetEventProperties_auto.go +++ b/sdk/event/GetEventProperties_auto.go @@ -7,9 +7,9 @@ package event import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/event" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/event" ) // Call_GetEventProperties forwards the call to dev.CallMethod() then parses the payload of the reply as a GetEventPropertiesResponse. diff --git a/sdk/event/GetServiceCapabilities_auto.go b/sdk/event/GetServiceCapabilities_auto.go index fa34bbf..df48a71 100644 --- a/sdk/event/GetServiceCapabilities_auto.go +++ b/sdk/event/GetServiceCapabilities_auto.go @@ -7,9 +7,9 @@ package event import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/event" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/event" ) // Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. diff --git a/sdk/event/Subscribe_auto.go b/sdk/event/Subscribe_auto.go index 2c5212d..6c41131 100644 --- a/sdk/event/Subscribe_auto.go +++ b/sdk/event/Subscribe_auto.go @@ -7,9 +7,9 @@ package event import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/event" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/event" ) // Call_Subscribe forwards the call to dev.CallMethod() then parses the payload of the reply as a SubscribeResponse. diff --git a/sdk/event/Unsubscribe_auto.go b/sdk/event/Unsubscribe_auto.go index d7f376b..0faa9ea 100644 --- a/sdk/event/Unsubscribe_auto.go +++ b/sdk/event/Unsubscribe_auto.go @@ -7,9 +7,9 @@ package event import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/event" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/event" ) // Call_Unsubscribe forwards the call to dev.CallMethod() then parses the payload of the reply as a UnsubscribeResponse. diff --git a/sdk/media/AddAudioDecoderConfiguration_auto.go b/sdk/media/AddAudioDecoderConfiguration_auto.go index 87a8088..f262669 100644 --- a/sdk/media/AddAudioDecoderConfiguration_auto.go +++ b/sdk/media/AddAudioDecoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioDecoderConfigurationResponse. diff --git a/sdk/media/AddAudioEncoderConfiguration_auto.go b/sdk/media/AddAudioEncoderConfiguration_auto.go index 3577280..5e616a3 100644 --- a/sdk/media/AddAudioEncoderConfiguration_auto.go +++ b/sdk/media/AddAudioEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioEncoderConfigurationResponse. diff --git a/sdk/media/AddAudioOutputConfiguration_auto.go b/sdk/media/AddAudioOutputConfiguration_auto.go index c403feb..8de8e86 100644 --- a/sdk/media/AddAudioOutputConfiguration_auto.go +++ b/sdk/media/AddAudioOutputConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioOutputConfigurationResponse. diff --git a/sdk/media/AddAudioSourceConfiguration_auto.go b/sdk/media/AddAudioSourceConfiguration_auto.go index 722d450..2b53799 100644 --- a/sdk/media/AddAudioSourceConfiguration_auto.go +++ b/sdk/media/AddAudioSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioSourceConfigurationResponse. diff --git a/sdk/media/AddMetadataConfiguration_auto.go b/sdk/media/AddMetadataConfiguration_auto.go index c564f4c..e2139a7 100644 --- a/sdk/media/AddMetadataConfiguration_auto.go +++ b/sdk/media/AddMetadataConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddMetadataConfigurationResponse. diff --git a/sdk/media/AddPTZConfiguration_auto.go b/sdk/media/AddPTZConfiguration_auto.go index bb4cac7..e3ed43d 100644 --- a/sdk/media/AddPTZConfiguration_auto.go +++ b/sdk/media/AddPTZConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddPTZConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddPTZConfigurationResponse. diff --git a/sdk/media/AddVideoAnalyticsConfiguration_auto.go b/sdk/media/AddVideoAnalyticsConfiguration_auto.go index 0f09f2d..c0c66da 100644 --- a/sdk/media/AddVideoAnalyticsConfiguration_auto.go +++ b/sdk/media/AddVideoAnalyticsConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddVideoAnalyticsConfigurationResponse. diff --git a/sdk/media/AddVideoEncoderConfiguration_auto.go b/sdk/media/AddVideoEncoderConfiguration_auto.go index 3035401..c5b5764 100644 --- a/sdk/media/AddVideoEncoderConfiguration_auto.go +++ b/sdk/media/AddVideoEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddVideoEncoderConfigurationResponse. diff --git a/sdk/media/AddVideoSourceConfiguration_auto.go b/sdk/media/AddVideoSourceConfiguration_auto.go index fd4654f..8f67d80 100644 --- a/sdk/media/AddVideoSourceConfiguration_auto.go +++ b/sdk/media/AddVideoSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_AddVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddVideoSourceConfigurationResponse. diff --git a/sdk/media/CreateOSD_auto.go b/sdk/media/CreateOSD_auto.go index 642e1b5..8fa9641 100644 --- a/sdk/media/CreateOSD_auto.go +++ b/sdk/media/CreateOSD_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_CreateOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateOSDResponse. diff --git a/sdk/media/CreateProfile_auto.go b/sdk/media/CreateProfile_auto.go index a009080..401e913 100644 --- a/sdk/media/CreateProfile_auto.go +++ b/sdk/media/CreateProfile_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_CreateProfile forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateProfileResponse. diff --git a/sdk/media/DeleteOSD_auto.go b/sdk/media/DeleteOSD_auto.go index 5510d74..896604c 100644 --- a/sdk/media/DeleteOSD_auto.go +++ b/sdk/media/DeleteOSD_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_DeleteOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteOSDResponse. diff --git a/sdk/media/DeleteProfile_auto.go b/sdk/media/DeleteProfile_auto.go index f59cd18..16e1564 100644 --- a/sdk/media/DeleteProfile_auto.go +++ b/sdk/media/DeleteProfile_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_DeleteProfile forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteProfileResponse. diff --git a/sdk/media/GetAudioDecoderConfigurationOptions_auto.go b/sdk/media/GetAudioDecoderConfigurationOptions_auto.go index 6668610..e396d37 100644 --- a/sdk/media/GetAudioDecoderConfigurationOptions_auto.go +++ b/sdk/media/GetAudioDecoderConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioDecoderConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioDecoderConfigurationOptionsResponse. diff --git a/sdk/media/GetAudioDecoderConfiguration_auto.go b/sdk/media/GetAudioDecoderConfiguration_auto.go index c93726b..0879aaa 100644 --- a/sdk/media/GetAudioDecoderConfiguration_auto.go +++ b/sdk/media/GetAudioDecoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioDecoderConfigurationResponse. diff --git a/sdk/media/GetAudioDecoderConfigurations_auto.go b/sdk/media/GetAudioDecoderConfigurations_auto.go index b71c20f..899aef9 100644 --- a/sdk/media/GetAudioDecoderConfigurations_auto.go +++ b/sdk/media/GetAudioDecoderConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioDecoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioDecoderConfigurationsResponse. diff --git a/sdk/media/GetAudioEncoderConfigurationOptions_auto.go b/sdk/media/GetAudioEncoderConfigurationOptions_auto.go index 0e4941b..5b7e718 100644 --- a/sdk/media/GetAudioEncoderConfigurationOptions_auto.go +++ b/sdk/media/GetAudioEncoderConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioEncoderConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioEncoderConfigurationOptionsResponse. diff --git a/sdk/media/GetAudioEncoderConfiguration_auto.go b/sdk/media/GetAudioEncoderConfiguration_auto.go index 6dd6d33..7dd8029 100644 --- a/sdk/media/GetAudioEncoderConfiguration_auto.go +++ b/sdk/media/GetAudioEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioEncoderConfigurationResponse. diff --git a/sdk/media/GetAudioEncoderConfigurations_auto.go b/sdk/media/GetAudioEncoderConfigurations_auto.go index b73f05b..62cea4b 100644 --- a/sdk/media/GetAudioEncoderConfigurations_auto.go +++ b/sdk/media/GetAudioEncoderConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioEncoderConfigurationsResponse. diff --git a/sdk/media/GetAudioOutputConfigurationOptions_auto.go b/sdk/media/GetAudioOutputConfigurationOptions_auto.go index d4f825f..db0f4a1 100644 --- a/sdk/media/GetAudioOutputConfigurationOptions_auto.go +++ b/sdk/media/GetAudioOutputConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioOutputConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputConfigurationOptionsResponse. diff --git a/sdk/media/GetAudioOutputConfiguration_auto.go b/sdk/media/GetAudioOutputConfiguration_auto.go index 3298318..9292b8b 100644 --- a/sdk/media/GetAudioOutputConfiguration_auto.go +++ b/sdk/media/GetAudioOutputConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputConfigurationResponse. diff --git a/sdk/media/GetAudioOutputConfigurations_auto.go b/sdk/media/GetAudioOutputConfigurations_auto.go index 31c7895..be284cd 100644 --- a/sdk/media/GetAudioOutputConfigurations_auto.go +++ b/sdk/media/GetAudioOutputConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioOutputConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputConfigurationsResponse. diff --git a/sdk/media/GetAudioOutputs_auto.go b/sdk/media/GetAudioOutputs_auto.go index 0bc2f9e..50cc9b3 100644 --- a/sdk/media/GetAudioOutputs_auto.go +++ b/sdk/media/GetAudioOutputs_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioOutputs forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputsResponse. diff --git a/sdk/media/GetAudioSourceConfigurationOptions_auto.go b/sdk/media/GetAudioSourceConfigurationOptions_auto.go index 77317da..a9584ea 100644 --- a/sdk/media/GetAudioSourceConfigurationOptions_auto.go +++ b/sdk/media/GetAudioSourceConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioSourceConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourceConfigurationOptionsResponse. diff --git a/sdk/media/GetAudioSourceConfiguration_auto.go b/sdk/media/GetAudioSourceConfiguration_auto.go index 56f9769..8d287e8 100644 --- a/sdk/media/GetAudioSourceConfiguration_auto.go +++ b/sdk/media/GetAudioSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourceConfigurationResponse. diff --git a/sdk/media/GetAudioSourceConfigurations_auto.go b/sdk/media/GetAudioSourceConfigurations_auto.go index 244a2bf..d29ca2e 100644 --- a/sdk/media/GetAudioSourceConfigurations_auto.go +++ b/sdk/media/GetAudioSourceConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourceConfigurationsResponse. diff --git a/sdk/media/GetAudioSources_auto.go b/sdk/media/GetAudioSources_auto.go index 14ae3b4..833a54f 100644 --- a/sdk/media/GetAudioSources_auto.go +++ b/sdk/media/GetAudioSources_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetAudioSources forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourcesResponse. diff --git a/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go b/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go index bb82699..1d2e171 100644 --- a/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go +++ b/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleAudioDecoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioDecoderConfigurationsResponse. diff --git a/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go b/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go index 9925115..dcf1c3c 100644 --- a/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go +++ b/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleAudioEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioEncoderConfigurationsResponse. diff --git a/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go b/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go index 6f70ed5..d88cb06 100644 --- a/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go +++ b/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleAudioOutputConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioOutputConfigurationsResponse. diff --git a/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go b/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go index 3957c41..b40e395 100644 --- a/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go +++ b/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleAudioSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioSourceConfigurationsResponse. diff --git a/sdk/media/GetCompatibleMetadataConfigurations_auto.go b/sdk/media/GetCompatibleMetadataConfigurations_auto.go index 44db0ce..6791715 100644 --- a/sdk/media/GetCompatibleMetadataConfigurations_auto.go +++ b/sdk/media/GetCompatibleMetadataConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleMetadataConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleMetadataConfigurationsResponse. diff --git a/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go b/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go index 3921387..4ef36ab 100644 --- a/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go +++ b/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleVideoAnalyticsConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleVideoAnalyticsConfigurationsResponse. diff --git a/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go b/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go index 8fd3aaf..2ddf7a8 100644 --- a/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go +++ b/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleVideoEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleVideoEncoderConfigurationsResponse. diff --git a/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go b/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go index 75539ca..80d3133 100644 --- a/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go +++ b/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetCompatibleVideoSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleVideoSourceConfigurationsResponse. diff --git a/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go b/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go index f4559f4..b3535d8 100644 --- a/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go +++ b/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetGuaranteedNumberOfVideoEncoderInstances forwards the call to dev.CallMethod() then parses the payload of the reply as a GetGuaranteedNumberOfVideoEncoderInstancesResponse. diff --git a/sdk/media/GetMetadataConfigurationOptions_auto.go b/sdk/media/GetMetadataConfigurationOptions_auto.go index 4be7747..ad67d6b 100644 --- a/sdk/media/GetMetadataConfigurationOptions_auto.go +++ b/sdk/media/GetMetadataConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetMetadataConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetMetadataConfigurationOptionsResponse. diff --git a/sdk/media/GetMetadataConfiguration_auto.go b/sdk/media/GetMetadataConfiguration_auto.go index 1c78949..94af08b 100644 --- a/sdk/media/GetMetadataConfiguration_auto.go +++ b/sdk/media/GetMetadataConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetMetadataConfigurationResponse. diff --git a/sdk/media/GetMetadataConfigurations_auto.go b/sdk/media/GetMetadataConfigurations_auto.go index 3435881..13d3141 100644 --- a/sdk/media/GetMetadataConfigurations_auto.go +++ b/sdk/media/GetMetadataConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetMetadataConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetMetadataConfigurationsResponse. diff --git a/sdk/media/GetOSDOptions_auto.go b/sdk/media/GetOSDOptions_auto.go index fa56d2e..d92d20a 100644 --- a/sdk/media/GetOSDOptions_auto.go +++ b/sdk/media/GetOSDOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetOSDOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetOSDOptionsResponse. diff --git a/sdk/media/GetOSD_auto.go b/sdk/media/GetOSD_auto.go index 4bbe7d5..6a5483d 100644 --- a/sdk/media/GetOSD_auto.go +++ b/sdk/media/GetOSD_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a GetOSDResponse. diff --git a/sdk/media/GetOSDs_auto.go b/sdk/media/GetOSDs_auto.go index 89c461f..f6a713c 100644 --- a/sdk/media/GetOSDs_auto.go +++ b/sdk/media/GetOSDs_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetOSDs forwards the call to dev.CallMethod() then parses the payload of the reply as a GetOSDsResponse. diff --git a/sdk/media/GetProfile_auto.go b/sdk/media/GetProfile_auto.go index ca026e4..d43b9cb 100644 --- a/sdk/media/GetProfile_auto.go +++ b/sdk/media/GetProfile_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetProfile forwards the call to dev.CallMethod() then parses the payload of the reply as a GetProfileResponse. diff --git a/sdk/media/GetProfiles_auto.go b/sdk/media/GetProfiles_auto.go index 0100f55..b081b46 100644 --- a/sdk/media/GetProfiles_auto.go +++ b/sdk/media/GetProfiles_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetProfiles forwards the call to dev.CallMethod() then parses the payload of the reply as a GetProfilesResponse. diff --git a/sdk/media/GetServiceCapabilities_auto.go b/sdk/media/GetServiceCapabilities_auto.go index eca9ae1..9459292 100644 --- a/sdk/media/GetServiceCapabilities_auto.go +++ b/sdk/media/GetServiceCapabilities_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. diff --git a/sdk/media/GetSnapshotUri_auto.go b/sdk/media/GetSnapshotUri_auto.go index 54a68c7..d325d37 100644 --- a/sdk/media/GetSnapshotUri_auto.go +++ b/sdk/media/GetSnapshotUri_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetSnapshotUri forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSnapshotUriResponse. diff --git a/sdk/media/GetStreamUri_auto.go b/sdk/media/GetStreamUri_auto.go index 1ecfc42..ebb345b 100644 --- a/sdk/media/GetStreamUri_auto.go +++ b/sdk/media/GetStreamUri_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetStreamUri forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStreamUriResponse. diff --git a/sdk/media/GetVideoAnalyticsConfiguration_auto.go b/sdk/media/GetVideoAnalyticsConfiguration_auto.go index cfc49f2..bbeb7d3 100644 --- a/sdk/media/GetVideoAnalyticsConfiguration_auto.go +++ b/sdk/media/GetVideoAnalyticsConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoAnalyticsConfigurationResponse. diff --git a/sdk/media/GetVideoAnalyticsConfigurations_auto.go b/sdk/media/GetVideoAnalyticsConfigurations_auto.go index 74fc35b..a2071aa 100644 --- a/sdk/media/GetVideoAnalyticsConfigurations_auto.go +++ b/sdk/media/GetVideoAnalyticsConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoAnalyticsConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoAnalyticsConfigurationsResponse. diff --git a/sdk/media/GetVideoEncoderConfigurationOptions_auto.go b/sdk/media/GetVideoEncoderConfigurationOptions_auto.go index cd1cc41..9380e74 100644 --- a/sdk/media/GetVideoEncoderConfigurationOptions_auto.go +++ b/sdk/media/GetVideoEncoderConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoEncoderConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoEncoderConfigurationOptionsResponse. diff --git a/sdk/media/GetVideoEncoderConfiguration_auto.go b/sdk/media/GetVideoEncoderConfiguration_auto.go index bcbb4e2..d5aece0 100644 --- a/sdk/media/GetVideoEncoderConfiguration_auto.go +++ b/sdk/media/GetVideoEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoEncoderConfigurationResponse. diff --git a/sdk/media/GetVideoEncoderConfigurations_auto.go b/sdk/media/GetVideoEncoderConfigurations_auto.go index 0b8a128..8932c92 100644 --- a/sdk/media/GetVideoEncoderConfigurations_auto.go +++ b/sdk/media/GetVideoEncoderConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoEncoderConfigurationsResponse. diff --git a/sdk/media/GetVideoSourceConfigurationOptions_auto.go b/sdk/media/GetVideoSourceConfigurationOptions_auto.go index ec3f70d..a079f98 100644 --- a/sdk/media/GetVideoSourceConfigurationOptions_auto.go +++ b/sdk/media/GetVideoSourceConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoSourceConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceConfigurationOptionsResponse. diff --git a/sdk/media/GetVideoSourceConfiguration_auto.go b/sdk/media/GetVideoSourceConfiguration_auto.go index 336bbdc..ef7c170 100644 --- a/sdk/media/GetVideoSourceConfiguration_auto.go +++ b/sdk/media/GetVideoSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceConfigurationResponse. diff --git a/sdk/media/GetVideoSourceConfigurations_auto.go b/sdk/media/GetVideoSourceConfigurations_auto.go index 9a93946..11b3dda 100644 --- a/sdk/media/GetVideoSourceConfigurations_auto.go +++ b/sdk/media/GetVideoSourceConfigurations_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceConfigurationsResponse. diff --git a/sdk/media/GetVideoSourceModes_auto.go b/sdk/media/GetVideoSourceModes_auto.go index 052e392..3ddc489 100644 --- a/sdk/media/GetVideoSourceModes_auto.go +++ b/sdk/media/GetVideoSourceModes_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoSourceModes forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceModesResponse. diff --git a/sdk/media/GetVideoSources_auto.go b/sdk/media/GetVideoSources_auto.go index c683fd8..9fadfaf 100644 --- a/sdk/media/GetVideoSources_auto.go +++ b/sdk/media/GetVideoSources_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_GetVideoSources forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourcesResponse. diff --git a/sdk/media/RemoveAudioDecoderConfiguration_auto.go b/sdk/media/RemoveAudioDecoderConfiguration_auto.go index aff1a8a..6ab5e32 100644 --- a/sdk/media/RemoveAudioDecoderConfiguration_auto.go +++ b/sdk/media/RemoveAudioDecoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioDecoderConfigurationResponse. diff --git a/sdk/media/RemoveAudioEncoderConfiguration_auto.go b/sdk/media/RemoveAudioEncoderConfiguration_auto.go index edbc89d..48db46d 100644 --- a/sdk/media/RemoveAudioEncoderConfiguration_auto.go +++ b/sdk/media/RemoveAudioEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioEncoderConfigurationResponse. diff --git a/sdk/media/RemoveAudioOutputConfiguration_auto.go b/sdk/media/RemoveAudioOutputConfiguration_auto.go index 51f1bcd..db04e13 100644 --- a/sdk/media/RemoveAudioOutputConfiguration_auto.go +++ b/sdk/media/RemoveAudioOutputConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioOutputConfigurationResponse. diff --git a/sdk/media/RemoveAudioSourceConfiguration_auto.go b/sdk/media/RemoveAudioSourceConfiguration_auto.go index 1aef6ca..95802e1 100644 --- a/sdk/media/RemoveAudioSourceConfiguration_auto.go +++ b/sdk/media/RemoveAudioSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioSourceConfigurationResponse. diff --git a/sdk/media/RemoveMetadataConfiguration_auto.go b/sdk/media/RemoveMetadataConfiguration_auto.go index 479359d..8d3a5f7 100644 --- a/sdk/media/RemoveMetadataConfiguration_auto.go +++ b/sdk/media/RemoveMetadataConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveMetadataConfigurationResponse. diff --git a/sdk/media/RemovePTZConfiguration_auto.go b/sdk/media/RemovePTZConfiguration_auto.go index 0b07f05..7f517f5 100644 --- a/sdk/media/RemovePTZConfiguration_auto.go +++ b/sdk/media/RemovePTZConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemovePTZConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemovePTZConfigurationResponse. diff --git a/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go b/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go index 88e7d60..3b61521 100644 --- a/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go +++ b/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveVideoAnalyticsConfigurationResponse. diff --git a/sdk/media/RemoveVideoEncoderConfiguration_auto.go b/sdk/media/RemoveVideoEncoderConfiguration_auto.go index 7a8745b..df3e048 100644 --- a/sdk/media/RemoveVideoEncoderConfiguration_auto.go +++ b/sdk/media/RemoveVideoEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveVideoEncoderConfigurationResponse. diff --git a/sdk/media/RemoveVideoSourceConfiguration_auto.go b/sdk/media/RemoveVideoSourceConfiguration_auto.go index b3ffb79..aaf6aca 100644 --- a/sdk/media/RemoveVideoSourceConfiguration_auto.go +++ b/sdk/media/RemoveVideoSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_RemoveVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveVideoSourceConfigurationResponse. diff --git a/sdk/media/SetAudioDecoderConfiguration_auto.go b/sdk/media/SetAudioDecoderConfiguration_auto.go index 47e34fe..0a9e4d9 100644 --- a/sdk/media/SetAudioDecoderConfiguration_auto.go +++ b/sdk/media/SetAudioDecoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioDecoderConfigurationResponse. diff --git a/sdk/media/SetAudioEncoderConfiguration_auto.go b/sdk/media/SetAudioEncoderConfiguration_auto.go index c60857d..2c9c281 100644 --- a/sdk/media/SetAudioEncoderConfiguration_auto.go +++ b/sdk/media/SetAudioEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioEncoderConfigurationResponse. diff --git a/sdk/media/SetAudioOutputConfiguration_auto.go b/sdk/media/SetAudioOutputConfiguration_auto.go index 1e2dd4b..41107f2 100644 --- a/sdk/media/SetAudioOutputConfiguration_auto.go +++ b/sdk/media/SetAudioOutputConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioOutputConfigurationResponse. diff --git a/sdk/media/SetAudioSourceConfiguration_auto.go b/sdk/media/SetAudioSourceConfiguration_auto.go index 5fd835c..81b092d 100644 --- a/sdk/media/SetAudioSourceConfiguration_auto.go +++ b/sdk/media/SetAudioSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioSourceConfigurationResponse. diff --git a/sdk/media/SetMetadataConfiguration_auto.go b/sdk/media/SetMetadataConfiguration_auto.go index 1baf90b..6da2cb6 100644 --- a/sdk/media/SetMetadataConfiguration_auto.go +++ b/sdk/media/SetMetadataConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetMetadataConfigurationResponse. diff --git a/sdk/media/SetOSD_auto.go b/sdk/media/SetOSD_auto.go index eb79fb9..5dd12ea 100644 --- a/sdk/media/SetOSD_auto.go +++ b/sdk/media/SetOSD_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a SetOSDResponse. diff --git a/sdk/media/SetSynchronizationPoint_auto.go b/sdk/media/SetSynchronizationPoint_auto.go index 79eedb8..8bc3289 100644 --- a/sdk/media/SetSynchronizationPoint_auto.go +++ b/sdk/media/SetSynchronizationPoint_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetSynchronizationPoint forwards the call to dev.CallMethod() then parses the payload of the reply as a SetSynchronizationPointResponse. diff --git a/sdk/media/SetVideoAnalyticsConfiguration_auto.go b/sdk/media/SetVideoAnalyticsConfiguration_auto.go index 172bf5a..6bc2b83 100644 --- a/sdk/media/SetVideoAnalyticsConfiguration_auto.go +++ b/sdk/media/SetVideoAnalyticsConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoAnalyticsConfigurationResponse. diff --git a/sdk/media/SetVideoEncoderConfiguration_auto.go b/sdk/media/SetVideoEncoderConfiguration_auto.go index 0a0727d..6aca6cf 100644 --- a/sdk/media/SetVideoEncoderConfiguration_auto.go +++ b/sdk/media/SetVideoEncoderConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoEncoderConfigurationResponse. diff --git a/sdk/media/SetVideoSourceConfiguration_auto.go b/sdk/media/SetVideoSourceConfiguration_auto.go index 6ee077a..5cc60e6 100644 --- a/sdk/media/SetVideoSourceConfiguration_auto.go +++ b/sdk/media/SetVideoSourceConfiguration_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoSourceConfigurationResponse. diff --git a/sdk/media/SetVideoSourceMode_auto.go b/sdk/media/SetVideoSourceMode_auto.go index 7cc0621..985be97 100644 --- a/sdk/media/SetVideoSourceMode_auto.go +++ b/sdk/media/SetVideoSourceMode_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_SetVideoSourceMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoSourceModeResponse. diff --git a/sdk/media/StartMulticastStreaming_auto.go b/sdk/media/StartMulticastStreaming_auto.go index 030c6e3..1e6f1ae 100644 --- a/sdk/media/StartMulticastStreaming_auto.go +++ b/sdk/media/StartMulticastStreaming_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_StartMulticastStreaming forwards the call to dev.CallMethod() then parses the payload of the reply as a StartMulticastStreamingResponse. diff --git a/sdk/media/StopMulticastStreaming_auto.go b/sdk/media/StopMulticastStreaming_auto.go index 118aa93..477ad40 100644 --- a/sdk/media/StopMulticastStreaming_auto.go +++ b/sdk/media/StopMulticastStreaming_auto.go @@ -7,9 +7,9 @@ package media import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/media" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/media" ) // Call_StopMulticastStreaming forwards the call to dev.CallMethod() then parses the payload of the reply as a StopMulticastStreamingResponse. diff --git a/sdk/media/media.go b/sdk/media/media.go index 93469f6..4ce49d7 100644 --- a/sdk/media/media.go +++ b/sdk/media/media.go @@ -1,81 +1,81 @@ package media -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetServiceCapabilities -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoSources -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioSources -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioOutputs -//go:generate go run github.com/use-go/onvif/sdk/codegen media media CreateProfile -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetProfile -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetProfiles -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddVideoEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveVideoEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddVideoSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveVideoSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddAudioEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveAudioEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddAudioSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveAudioSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddPTZConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemovePTZConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddVideoAnalyticsConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveVideoAnalyticsConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddMetadataConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveMetadataConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddAudioOutputConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveAudioOutputConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media AddAudioDecoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media RemoveAudioDecoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media DeleteProfile -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoSourceConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoEncoderConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioSourceConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioEncoderConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoAnalyticsConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetMetadataConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioOutputConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioDecoderConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoAnalyticsConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetMetadataConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioOutputConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioDecoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleVideoEncoderConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleVideoSourceConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleAudioEncoderConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleAudioSourceConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleVideoAnalyticsConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleMetadataConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleAudioOutputConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetCompatibleAudioDecoderConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetVideoSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetVideoEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetAudioSourceConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetAudioEncoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetVideoAnalyticsConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetMetadataConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetAudioOutputConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetAudioDecoderConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoSourceConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoEncoderConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioSourceConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioEncoderConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetMetadataConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioOutputConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetAudioDecoderConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetGuaranteedNumberOfVideoEncoderInstances -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetStreamUri -//go:generate go run github.com/use-go/onvif/sdk/codegen media media StartMulticastStreaming -//go:generate go run github.com/use-go/onvif/sdk/codegen media media StopMulticastStreaming -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetSynchronizationPoint -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetSnapshotUri -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetVideoSourceModes -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetVideoSourceMode -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetOSDs -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetOSD -//go:generate go run github.com/use-go/onvif/sdk/codegen media media GetOSDOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen media media SetOSD -//go:generate go run github.com/use-go/onvif/sdk/codegen media media CreateOSD -//go:generate go run github.com/use-go/onvif/sdk/codegen media media DeleteOSD +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetServiceCapabilities +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSources +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSources +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputs +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media CreateProfile +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetProfile +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetProfiles +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddVideoEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveVideoEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddVideoSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveVideoSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddPTZConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemovePTZConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddVideoAnalyticsConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveVideoAnalyticsConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddMetadataConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveMetadataConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioOutputConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioOutputConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioDecoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioDecoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media DeleteProfile +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoEncoderConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSourceConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioEncoderConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoAnalyticsConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetMetadataConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioDecoderConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoAnalyticsConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetMetadataConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioDecoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleVideoEncoderConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleVideoSourceConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioEncoderConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioSourceConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleVideoAnalyticsConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleMetadataConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioOutputConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioDecoderConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioSourceConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioEncoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoAnalyticsConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetMetadataConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioOutputConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioDecoderConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoEncoderConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSourceConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioEncoderConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetMetadataConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioDecoderConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetGuaranteedNumberOfVideoEncoderInstances +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetStreamUri +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media StartMulticastStreaming +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media StopMulticastStreaming +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetSynchronizationPoint +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetSnapshotUri +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceModes +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoSourceMode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetOSDs +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetOSD +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetOSDOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetOSD +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media CreateOSD +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media DeleteOSD diff --git a/sdk/ptz/AbsoluteMove_auto.go b/sdk/ptz/AbsoluteMove_auto.go index 99a6732..61a877c 100644 --- a/sdk/ptz/AbsoluteMove_auto.go +++ b/sdk/ptz/AbsoluteMove_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_AbsoluteMove forwards the call to dev.CallMethod() then parses the payload of the reply as a AbsoluteMoveResponse. diff --git a/sdk/ptz/ContinuousMove_auto.go b/sdk/ptz/ContinuousMove_auto.go index fa8eca9..beaf9ab 100644 --- a/sdk/ptz/ContinuousMove_auto.go +++ b/sdk/ptz/ContinuousMove_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_ContinuousMove forwards the call to dev.CallMethod() then parses the payload of the reply as a ContinuousMoveResponse. diff --git a/sdk/ptz/CreatePresetTour_auto.go b/sdk/ptz/CreatePresetTour_auto.go index fc99a5d..43bea08 100644 --- a/sdk/ptz/CreatePresetTour_auto.go +++ b/sdk/ptz/CreatePresetTour_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_CreatePresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a CreatePresetTourResponse. diff --git a/sdk/ptz/GeoMove_auto.go b/sdk/ptz/GeoMove_auto.go index 03c5c98..6c70105 100644 --- a/sdk/ptz/GeoMove_auto.go +++ b/sdk/ptz/GeoMove_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GeoMove forwards the call to dev.CallMethod() then parses the payload of the reply as a GeoMoveResponse. diff --git a/sdk/ptz/GetCompatibleConfigurations_auto.go b/sdk/ptz/GetCompatibleConfigurations_auto.go index 3e7c68c..8673ee2 100644 --- a/sdk/ptz/GetCompatibleConfigurations_auto.go +++ b/sdk/ptz/GetCompatibleConfigurations_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetCompatibleConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleConfigurationsResponse. diff --git a/sdk/ptz/GetConfigurationOptions_auto.go b/sdk/ptz/GetConfigurationOptions_auto.go index 02e2ee9..e9abd7d 100644 --- a/sdk/ptz/GetConfigurationOptions_auto.go +++ b/sdk/ptz/GetConfigurationOptions_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetConfigurationOptionsResponse. diff --git a/sdk/ptz/GetConfiguration_auto.go b/sdk/ptz/GetConfiguration_auto.go index 5b7e36a..852308e 100644 --- a/sdk/ptz/GetConfiguration_auto.go +++ b/sdk/ptz/GetConfiguration_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetConfigurationResponse. diff --git a/sdk/ptz/GetConfigurations_auto.go b/sdk/ptz/GetConfigurations_auto.go index 0cc1f15..41da137 100644 --- a/sdk/ptz/GetConfigurations_auto.go +++ b/sdk/ptz/GetConfigurations_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetConfigurationsResponse. diff --git a/sdk/ptz/GetNode_auto.go b/sdk/ptz/GetNode_auto.go index 399931d..7f46952 100644 --- a/sdk/ptz/GetNode_auto.go +++ b/sdk/ptz/GetNode_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetNode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNodeResponse. diff --git a/sdk/ptz/GetNodes_auto.go b/sdk/ptz/GetNodes_auto.go index 08689f9..84699b0 100644 --- a/sdk/ptz/GetNodes_auto.go +++ b/sdk/ptz/GetNodes_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetNodes forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNodesResponse. diff --git a/sdk/ptz/GetPresetTourOptions_auto.go b/sdk/ptz/GetPresetTourOptions_auto.go index 82b7220..2a63618 100644 --- a/sdk/ptz/GetPresetTourOptions_auto.go +++ b/sdk/ptz/GetPresetTourOptions_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetPresetTourOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetTourOptionsResponse. diff --git a/sdk/ptz/GetPresetTour_auto.go b/sdk/ptz/GetPresetTour_auto.go index 019fbf9..15fef28 100644 --- a/sdk/ptz/GetPresetTour_auto.go +++ b/sdk/ptz/GetPresetTour_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetPresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetTourResponse. diff --git a/sdk/ptz/GetPresetTours_auto.go b/sdk/ptz/GetPresetTours_auto.go index b9ad833..ea84858 100644 --- a/sdk/ptz/GetPresetTours_auto.go +++ b/sdk/ptz/GetPresetTours_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetPresetTours forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetToursResponse. diff --git a/sdk/ptz/GetPresets_auto.go b/sdk/ptz/GetPresets_auto.go index 9663655..1273f5c 100644 --- a/sdk/ptz/GetPresets_auto.go +++ b/sdk/ptz/GetPresets_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetPresets forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetsResponse. diff --git a/sdk/ptz/GetServiceCapabilities_auto.go b/sdk/ptz/GetServiceCapabilities_auto.go index c52bfec..4c6b7a3 100644 --- a/sdk/ptz/GetServiceCapabilities_auto.go +++ b/sdk/ptz/GetServiceCapabilities_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. diff --git a/sdk/ptz/GetStatus_auto.go b/sdk/ptz/GetStatus_auto.go index a8fc629..433df4d 100644 --- a/sdk/ptz/GetStatus_auto.go +++ b/sdk/ptz/GetStatus_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GetStatus forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStatusResponse. diff --git a/sdk/ptz/GotoHomePosition_auto.go b/sdk/ptz/GotoHomePosition_auto.go index 6b22230..ce6c22d 100644 --- a/sdk/ptz/GotoHomePosition_auto.go +++ b/sdk/ptz/GotoHomePosition_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GotoHomePosition forwards the call to dev.CallMethod() then parses the payload of the reply as a GotoHomePositionResponse. diff --git a/sdk/ptz/GotoPreset_auto.go b/sdk/ptz/GotoPreset_auto.go index bab80c7..28dac13 100644 --- a/sdk/ptz/GotoPreset_auto.go +++ b/sdk/ptz/GotoPreset_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_GotoPreset forwards the call to dev.CallMethod() then parses the payload of the reply as a GotoPresetResponse. diff --git a/sdk/ptz/ModifyPresetTour_auto.go b/sdk/ptz/ModifyPresetTour_auto.go index 5a6c3a4..2d0d6e0 100644 --- a/sdk/ptz/ModifyPresetTour_auto.go +++ b/sdk/ptz/ModifyPresetTour_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_ModifyPresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a ModifyPresetTourResponse. diff --git a/sdk/ptz/OperatePresetTour_auto.go b/sdk/ptz/OperatePresetTour_auto.go index 2e4d942..9e278d0 100644 --- a/sdk/ptz/OperatePresetTour_auto.go +++ b/sdk/ptz/OperatePresetTour_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_OperatePresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a OperatePresetTourResponse. diff --git a/sdk/ptz/RelativeMove_auto.go b/sdk/ptz/RelativeMove_auto.go index 5adce58..8a24189 100644 --- a/sdk/ptz/RelativeMove_auto.go +++ b/sdk/ptz/RelativeMove_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_RelativeMove forwards the call to dev.CallMethod() then parses the payload of the reply as a RelativeMoveResponse. diff --git a/sdk/ptz/RemovePresetTour_auto.go b/sdk/ptz/RemovePresetTour_auto.go index 136f568..ed22049 100644 --- a/sdk/ptz/RemovePresetTour_auto.go +++ b/sdk/ptz/RemovePresetTour_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_RemovePresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a RemovePresetTourResponse. diff --git a/sdk/ptz/RemovePreset_auto.go b/sdk/ptz/RemovePreset_auto.go index 1db7c99..ae4828f 100644 --- a/sdk/ptz/RemovePreset_auto.go +++ b/sdk/ptz/RemovePreset_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_RemovePreset forwards the call to dev.CallMethod() then parses the payload of the reply as a RemovePresetResponse. diff --git a/sdk/ptz/SendAuxiliaryCommand_auto.go b/sdk/ptz/SendAuxiliaryCommand_auto.go index 6ed6f1c..23a7fa4 100644 --- a/sdk/ptz/SendAuxiliaryCommand_auto.go +++ b/sdk/ptz/SendAuxiliaryCommand_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_SendAuxiliaryCommand forwards the call to dev.CallMethod() then parses the payload of the reply as a SendAuxiliaryCommandResponse. diff --git a/sdk/ptz/SetConfiguration_auto.go b/sdk/ptz/SetConfiguration_auto.go index 97be3b2..262be18 100644 --- a/sdk/ptz/SetConfiguration_auto.go +++ b/sdk/ptz/SetConfiguration_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_SetConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetConfigurationResponse. diff --git a/sdk/ptz/SetHomePosition_auto.go b/sdk/ptz/SetHomePosition_auto.go index 9f2722d..bef6888 100644 --- a/sdk/ptz/SetHomePosition_auto.go +++ b/sdk/ptz/SetHomePosition_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_SetHomePosition forwards the call to dev.CallMethod() then parses the payload of the reply as a SetHomePositionResponse. diff --git a/sdk/ptz/SetPreset_auto.go b/sdk/ptz/SetPreset_auto.go index 020e73f..d319dbb 100644 --- a/sdk/ptz/SetPreset_auto.go +++ b/sdk/ptz/SetPreset_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_SetPreset forwards the call to dev.CallMethod() then parses the payload of the reply as a SetPresetResponse. diff --git a/sdk/ptz/Stop_auto.go b/sdk/ptz/Stop_auto.go index 12f6e54..dc21364 100644 --- a/sdk/ptz/Stop_auto.go +++ b/sdk/ptz/Stop_auto.go @@ -7,9 +7,9 @@ package ptz import ( "context" "github.com/juju/errors" - "github.com/use-go/onvif" - "github.com/use-go/onvif/sdk" - "github.com/use-go/onvif/ptz" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/sdk" + "github.com/kerberos-io/onvif/ptz" ) // Call_Stop forwards the call to dev.CallMethod() then parses the payload of the reply as a StopResponse. diff --git a/sdk/ptz/ptz.go b/sdk/ptz/ptz.go index 910b28f..40b7ee2 100644 --- a/sdk/ptz/ptz.go +++ b/sdk/ptz/ptz.go @@ -1,30 +1,30 @@ package ptz -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetServiceCapabilities -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetNodes -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetNode -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetConfigurations -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz SetConfiguration -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetConfigurationOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz SendAuxiliaryCommand -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetPresets -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz SetPreset -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz RemovePreset -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GotoPreset -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GotoHomePosition -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz SetHomePosition -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz ContinuousMove -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz RelativeMove -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetStatus -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz AbsoluteMove -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GeoMove -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz Stop -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetPresetTours -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetPresetTour -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetPresetTourOptions -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz CreatePresetTour -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz ModifyPresetTour -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz OperatePresetTour -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz RemovePresetTour -//go:generate go run github.com/use-go/onvif/sdk/codegen ptz ptz GetCompatibleConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetServiceCapabilities +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetNodes +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetNode +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetConfigurations +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SetConfiguration +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetConfigurationOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SendAuxiliaryCommand +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresets +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SetPreset +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz RemovePreset +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GotoPreset +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GotoHomePosition +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SetHomePosition +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz ContinuousMove +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz RelativeMove +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetStatus +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz AbsoluteMove +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GeoMove +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz Stop +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresetTours +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresetTour +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresetTourOptions +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz CreatePresetTour +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz ModifyPresetTour +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz OperatePresetTour +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz RemovePresetTour +//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetCompatibleConfigurations From 8902e4e789ff1a0a711c818e0a3151477618869f Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Mon, 18 Dec 2023 20:16:21 +0100 Subject: [PATCH 07/53] upgrade onvif library --- .github/dependabot.yml | 7 + .gitignore | 3 + .idea/.gitignore | 8 - .idea/modules.xml | 8 - .idea/onvif.iml | 9 - .idea/vcs.xml | 6 - .vscode/settings.json | 4 +- Device.go | 293 ++++-- Device_test.go | 24 + Imaging/types.go | 44 + README.md | 35 +- analytics/common.go | 47 + analytics/function.go | 117 +++ analytics/types.go | 231 ++++- api/api.go | 137 +-- cmd/camera-example/main.go | 132 +++ constant.go | 9 + device/function.go | 819 +++++++++++++++ device/types.go | 187 ++-- deviceio/function.go | 819 +++++++++++++++ deviceio/types.go | 913 +++++++++++++++++ digestclient.go | 111 ++ doc.go | 2 +- docs/Development.md | 34 + event/function.go | 99 ++ event/operation.go | 130 --- event/topic/description.go | 22 + event/topic/ruleengine.go | 63 ++ event/type_test.go | 92 ++ event/types.go | 236 ++++- examples/DeviceService.go | 44 +- .../getanalyticsconfigurations/main.go | 28 + examples/analytic/getprofiles/main.go | 28 + examples/discovery_test.go | 18 +- examples/event/createpullpoint/main.go | 87 ++ examples/event/eventproperties/main.go | 28 + examples/event/pullmessage/main.go | 92 ++ examples/event/renew/main.go | 94 ++ examples/event/subscribe/main.go | 110 ++ examples/event/unsubscribe/main.go | 75 ++ examples/getusers/main.go | 27 + functionmap.go | 36 + go.mod | 42 +- go.sum | 163 ++- gosoap/envelope.go | 121 +++ gosoap/soap-builder.go | 4 +- gosoap/ws-action.go | 2 +- gosoap/ws-security.go | 14 +- imaging/function.go | 108 ++ interfaces.go | 6 + mappings.go | 299 ++++++ media/function.go | 720 +++++++++++++ media/types.go | 36 +- media2/function.go | 45 + media2/types.go | 93 ++ media2/types_test.go | 134 +++ names.go | 297 ++++++ networking/networking.go | 4 +- ptz/function.go | 261 +++++ ptz/types.go | 62 +- python/Makefile | 11 + python/gen_commands.py | 149 +++ recording/function.go | 198 ++++ recording/types.go | 793 +++++++++++++++ sdk/codegen/main.go | 78 -- sdk/device/AddIPAddressFilter_auto.go | 30 - sdk/device/AddScopes_auto.go | 30 - sdk/device/CreateCertificate_auto.go | 30 - sdk/device/CreateDot1XConfiguration_auto.go | 30 - sdk/device/CreateStorageConfiguration_auto.go | 30 - sdk/device/CreateUsers_auto.go | 30 - sdk/device/DeleteCertificates_auto.go | 30 - sdk/device/DeleteDot1XConfiguration_auto.go | 30 - sdk/device/DeleteGeoLocation_auto.go | 30 - sdk/device/DeleteStorageConfiguration_auto.go | 30 - sdk/device/DeleteUsers_auto.go | 30 - sdk/device/GetAccessPolicy_auto.go | 30 - sdk/device/GetCACertificates_auto.go | 30 - sdk/device/GetCapabilities_auto.go | 30 - sdk/device/GetCertificateInformation_auto.go | 30 - sdk/device/GetCertificatesStatus_auto.go | 30 - sdk/device/GetCertificates_auto.go | 30 - sdk/device/GetClientCertificateMode_auto.go | 30 - sdk/device/GetDNS_auto.go | 30 - sdk/device/GetDPAddresses_auto.go | 30 - sdk/device/GetDeviceInformation_auto.go | 30 - sdk/device/GetDiscoveryMode_auto.go | 30 - sdk/device/GetDot11Capabilities_auto.go | 30 - sdk/device/GetDot11Status_auto.go | 30 - sdk/device/GetDot1XConfiguration_auto.go | 30 - sdk/device/GetDot1XConfigurations_auto.go | 30 - sdk/device/GetDynamicDNS_auto.go | 30 - sdk/device/GetEndpointReference_auto.go | 30 - sdk/device/GetGeoLocation_auto.go | 30 - sdk/device/GetHostname_auto.go | 30 - sdk/device/GetIPAddressFilter_auto.go | 30 - sdk/device/GetNTP_auto.go | 30 - sdk/device/GetNetworkDefaultGateway_auto.go | 30 - sdk/device/GetNetworkInterfaces_auto.go | 30 - sdk/device/GetNetworkProtocols_auto.go | 30 - sdk/device/GetPkcs10Request_auto.go | 30 - sdk/device/GetRelayOutputs_auto.go | 30 - sdk/device/GetRemoteDiscoveryMode_auto.go | 30 - sdk/device/GetRemoteUser_auto.go | 30 - sdk/device/GetScopes_auto.go | 30 - sdk/device/GetServiceCapabilities_auto.go | 30 - sdk/device/GetServices_auto.go | 30 - sdk/device/GetStorageConfiguration_auto.go | 30 - sdk/device/GetStorageConfigurations_auto.go | 30 - sdk/device/GetSystemBackup_auto.go | 30 - sdk/device/GetSystemDateAndTime_auto.go | 30 - sdk/device/GetSystemLog_auto.go | 30 - .../GetSystemSupportInformation_auto.go | 30 - sdk/device/GetSystemUris_auto.go | 30 - sdk/device/GetUsers_auto.go | 30 - sdk/device/GetWsdlUrl_auto.go | 30 - sdk/device/GetZeroConfiguration_auto.go | 30 - sdk/device/LoadCACertificates_auto.go | 30 - .../LoadCertificateWithPrivateKey_auto.go | 30 - sdk/device/LoadCertificates_auto.go | 30 - sdk/device/RemoveIPAddressFilter_auto.go | 30 - sdk/device/RemoveScopes_auto.go | 30 - sdk/device/RestoreSystem_auto.go | 30 - sdk/device/ScanAvailableDot11Networks_auto.go | 30 - sdk/device/SendAuxiliaryCommand_auto.go | 30 - sdk/device/SetAccessPolicy_auto.go | 30 - sdk/device/SetCertificatesStatus_auto.go | 30 - sdk/device/SetClientCertificateMode_auto.go | 30 - sdk/device/SetDNS_auto.go | 30 - sdk/device/SetDiscoveryMode_auto.go | 30 - sdk/device/SetDot1XConfiguration_auto.go | 30 - sdk/device/SetDynamicDNS_auto.go | 30 - sdk/device/SetGeoLocation_auto.go | 30 - sdk/device/SetHostnameFromDHCP_auto.go | 30 - sdk/device/SetHostname_auto.go | 30 - sdk/device/SetIPAddressFilter_auto.go | 30 - sdk/device/SetNTP_auto.go | 30 - sdk/device/SetNetworkDefaultGateway_auto.go | 30 - sdk/device/SetNetworkInterfaces_auto.go | 30 - sdk/device/SetNetworkProtocols_auto.go | 30 - sdk/device/SetRelayOutputSettings_auto.go | 30 - sdk/device/SetRelayOutputState_auto.go | 30 - sdk/device/SetRemoteDiscoveryMode_auto.go | 30 - sdk/device/SetRemoteUser_auto.go | 30 - sdk/device/SetScopes_auto.go | 30 - sdk/device/SetStorageConfiguration_auto.go | 30 - sdk/device/SetSystemDateAndTime_auto.go | 30 - sdk/device/SetSystemFactoryDefault_auto.go | 30 - sdk/device/SetUser_auto.go | 30 - sdk/device/SetZeroConfiguration_auto.go | 30 - sdk/device/StartFirmwareUpgrade_auto.go | 30 - sdk/device/StartSystemRestore_auto.go | 30 - sdk/device/SystemReboot_auto.go | 30 - sdk/device/UpgradeSystemFirmware_auto.go | 30 - sdk/device/device.go | 91 -- sdk/event/CreatePullPointSubscription_auto.go | 30 - sdk/event/GetEventProperties_auto.go | 30 - sdk/event/GetServiceCapabilities_auto.go | 30 - sdk/event/Subscribe_auto.go | 30 - sdk/event/Unsubscribe_auto.go | 30 - .../AddAudioDecoderConfiguration_auto.go | 30 - .../AddAudioEncoderConfiguration_auto.go | 30 - sdk/media/AddAudioOutputConfiguration_auto.go | 30 - sdk/media/AddAudioSourceConfiguration_auto.go | 30 - sdk/media/AddMetadataConfiguration_auto.go | 30 - sdk/media/AddPTZConfiguration_auto.go | 30 - .../AddVideoAnalyticsConfiguration_auto.go | 30 - .../AddVideoEncoderConfiguration_auto.go | 30 - sdk/media/AddVideoSourceConfiguration_auto.go | 30 - sdk/media/CreateOSD_auto.go | 30 - sdk/media/CreateProfile_auto.go | 30 - sdk/media/DeleteOSD_auto.go | 30 - sdk/media/DeleteProfile_auto.go | 30 - ...etAudioDecoderConfigurationOptions_auto.go | 30 - .../GetAudioDecoderConfiguration_auto.go | 30 - .../GetAudioDecoderConfigurations_auto.go | 30 - ...etAudioEncoderConfigurationOptions_auto.go | 30 - .../GetAudioEncoderConfiguration_auto.go | 30 - .../GetAudioEncoderConfigurations_auto.go | 30 - ...GetAudioOutputConfigurationOptions_auto.go | 30 - sdk/media/GetAudioOutputConfiguration_auto.go | 30 - .../GetAudioOutputConfigurations_auto.go | 30 - sdk/media/GetAudioOutputs_auto.go | 30 - ...GetAudioSourceConfigurationOptions_auto.go | 30 - sdk/media/GetAudioSourceConfiguration_auto.go | 30 - .../GetAudioSourceConfigurations_auto.go | 30 - sdk/media/GetAudioSources_auto.go | 30 - ...mpatibleAudioDecoderConfigurations_auto.go | 30 - ...mpatibleAudioEncoderConfigurations_auto.go | 30 - ...ompatibleAudioOutputConfigurations_auto.go | 30 - ...ompatibleAudioSourceConfigurations_auto.go | 30 - ...etCompatibleMetadataConfigurations_auto.go | 30 - ...atibleVideoAnalyticsConfigurations_auto.go | 30 - ...mpatibleVideoEncoderConfigurations_auto.go | 30 - ...ompatibleVideoSourceConfigurations_auto.go | 30 - ...nteedNumberOfVideoEncoderInstances_auto.go | 30 - .../GetMetadataConfigurationOptions_auto.go | 30 - sdk/media/GetMetadataConfiguration_auto.go | 30 - sdk/media/GetMetadataConfigurations_auto.go | 30 - sdk/media/GetOSDOptions_auto.go | 30 - sdk/media/GetOSD_auto.go | 30 - sdk/media/GetOSDs_auto.go | 30 - sdk/media/GetProfile_auto.go | 30 - sdk/media/GetProfiles_auto.go | 30 - sdk/media/GetServiceCapabilities_auto.go | 30 - sdk/media/GetSnapshotUri_auto.go | 30 - sdk/media/GetStreamUri_auto.go | 30 - .../GetVideoAnalyticsConfiguration_auto.go | 30 - .../GetVideoAnalyticsConfigurations_auto.go | 30 - ...etVideoEncoderConfigurationOptions_auto.go | 30 - .../GetVideoEncoderConfiguration_auto.go | 30 - .../GetVideoEncoderConfigurations_auto.go | 30 - ...GetVideoSourceConfigurationOptions_auto.go | 30 - sdk/media/GetVideoSourceConfiguration_auto.go | 30 - .../GetVideoSourceConfigurations_auto.go | 30 - sdk/media/GetVideoSourceModes_auto.go | 30 - sdk/media/GetVideoSources_auto.go | 30 - .../RemoveAudioDecoderConfiguration_auto.go | 30 - .../RemoveAudioEncoderConfiguration_auto.go | 30 - .../RemoveAudioOutputConfiguration_auto.go | 30 - .../RemoveAudioSourceConfiguration_auto.go | 30 - sdk/media/RemoveMetadataConfiguration_auto.go | 30 - sdk/media/RemovePTZConfiguration_auto.go | 30 - .../RemoveVideoAnalyticsConfiguration_auto.go | 30 - .../RemoveVideoEncoderConfiguration_auto.go | 30 - .../RemoveVideoSourceConfiguration_auto.go | 30 - .../SetAudioDecoderConfiguration_auto.go | 30 - .../SetAudioEncoderConfiguration_auto.go | 30 - sdk/media/SetAudioOutputConfiguration_auto.go | 30 - sdk/media/SetAudioSourceConfiguration_auto.go | 30 - sdk/media/SetMetadataConfiguration_auto.go | 30 - sdk/media/SetOSD_auto.go | 30 - sdk/media/SetSynchronizationPoint_auto.go | 30 - .../SetVideoAnalyticsConfiguration_auto.go | 30 - .../SetVideoEncoderConfiguration_auto.go | 30 - sdk/media/SetVideoSourceConfiguration_auto.go | 30 - sdk/media/SetVideoSourceMode_auto.go | 30 - sdk/media/StartMulticastStreaming_auto.go | 30 - sdk/media/StopMulticastStreaming_auto.go | 30 - sdk/media/media.go | 81 -- sdk/ptz/AbsoluteMove_auto.go | 30 - sdk/ptz/ContinuousMove_auto.go | 30 - sdk/ptz/CreatePresetTour_auto.go | 30 - sdk/ptz/GeoMove_auto.go | 30 - sdk/ptz/GetCompatibleConfigurations_auto.go | 30 - sdk/ptz/GetConfigurationOptions_auto.go | 30 - sdk/ptz/GetConfiguration_auto.go | 30 - sdk/ptz/GetConfigurations_auto.go | 30 - sdk/ptz/GetNode_auto.go | 30 - sdk/ptz/GetNodes_auto.go | 30 - sdk/ptz/GetPresetTourOptions_auto.go | 30 - sdk/ptz/GetPresetTour_auto.go | 30 - sdk/ptz/GetPresetTours_auto.go | 30 - sdk/ptz/GetPresets_auto.go | 30 - sdk/ptz/GetServiceCapabilities_auto.go | 30 - sdk/ptz/GetStatus_auto.go | 30 - sdk/ptz/GotoHomePosition_auto.go | 30 - sdk/ptz/GotoPreset_auto.go | 30 - sdk/ptz/ModifyPresetTour_auto.go | 30 - sdk/ptz/OperatePresetTour_auto.go | 30 - sdk/ptz/RelativeMove_auto.go | 30 - sdk/ptz/RemovePresetTour_auto.go | 30 - sdk/ptz/RemovePreset_auto.go | 30 - sdk/ptz/SendAuxiliaryCommand_auto.go | 30 - sdk/ptz/SetConfiguration_auto.go | 30 - sdk/ptz/SetHomePosition_auto.go | 30 - sdk/ptz/SetPreset_auto.go | 30 - sdk/ptz/Stop_auto.go | 30 - sdk/ptz/ptz.go | 30 - sdk/sdk.go | 43 - ws-discovery/networking.go | 178 +++- ws-discovery/networking_test.go | 56 + ws-discovery/ws-discovery.go | 73 +- xsd/built_in.go | 448 ++++---- xsd/onvif/onvif.go | 958 ++++++++++-------- 275 files changed, 9225 insertions(+), 7711 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .gitignore delete mode 100644 .idea/.gitignore delete mode 100644 .idea/modules.xml delete mode 100644 .idea/onvif.iml delete mode 100644 .idea/vcs.xml create mode 100644 Device_test.go create mode 100644 analytics/common.go create mode 100644 analytics/function.go create mode 100644 cmd/camera-example/main.go create mode 100644 constant.go create mode 100644 device/function.go create mode 100644 deviceio/function.go create mode 100644 deviceio/types.go create mode 100644 digestclient.go create mode 100644 docs/Development.md create mode 100644 event/function.go delete mode 100644 event/operation.go create mode 100644 event/topic/description.go create mode 100644 event/topic/ruleengine.go create mode 100644 event/type_test.go create mode 100644 examples/analytic/getanalyticsconfigurations/main.go create mode 100644 examples/analytic/getprofiles/main.go create mode 100644 examples/event/createpullpoint/main.go create mode 100644 examples/event/eventproperties/main.go create mode 100644 examples/event/pullmessage/main.go create mode 100644 examples/event/renew/main.go create mode 100644 examples/event/subscribe/main.go create mode 100644 examples/event/unsubscribe/main.go create mode 100644 examples/getusers/main.go create mode 100644 functionmap.go create mode 100644 gosoap/envelope.go create mode 100644 imaging/function.go create mode 100644 interfaces.go create mode 100644 mappings.go create mode 100644 media/function.go create mode 100644 media2/function.go create mode 100644 media2/types.go create mode 100644 media2/types_test.go create mode 100644 names.go create mode 100644 ptz/function.go create mode 100644 python/Makefile create mode 100755 python/gen_commands.py create mode 100644 recording/function.go create mode 100644 recording/types.go delete mode 100644 sdk/codegen/main.go delete mode 100644 sdk/device/AddIPAddressFilter_auto.go delete mode 100644 sdk/device/AddScopes_auto.go delete mode 100644 sdk/device/CreateCertificate_auto.go delete mode 100644 sdk/device/CreateDot1XConfiguration_auto.go delete mode 100644 sdk/device/CreateStorageConfiguration_auto.go delete mode 100644 sdk/device/CreateUsers_auto.go delete mode 100644 sdk/device/DeleteCertificates_auto.go delete mode 100644 sdk/device/DeleteDot1XConfiguration_auto.go delete mode 100644 sdk/device/DeleteGeoLocation_auto.go delete mode 100644 sdk/device/DeleteStorageConfiguration_auto.go delete mode 100644 sdk/device/DeleteUsers_auto.go delete mode 100644 sdk/device/GetAccessPolicy_auto.go delete mode 100644 sdk/device/GetCACertificates_auto.go delete mode 100644 sdk/device/GetCapabilities_auto.go delete mode 100644 sdk/device/GetCertificateInformation_auto.go delete mode 100644 sdk/device/GetCertificatesStatus_auto.go delete mode 100644 sdk/device/GetCertificates_auto.go delete mode 100644 sdk/device/GetClientCertificateMode_auto.go delete mode 100644 sdk/device/GetDNS_auto.go delete mode 100644 sdk/device/GetDPAddresses_auto.go delete mode 100644 sdk/device/GetDeviceInformation_auto.go delete mode 100644 sdk/device/GetDiscoveryMode_auto.go delete mode 100644 sdk/device/GetDot11Capabilities_auto.go delete mode 100644 sdk/device/GetDot11Status_auto.go delete mode 100644 sdk/device/GetDot1XConfiguration_auto.go delete mode 100644 sdk/device/GetDot1XConfigurations_auto.go delete mode 100644 sdk/device/GetDynamicDNS_auto.go delete mode 100644 sdk/device/GetEndpointReference_auto.go delete mode 100644 sdk/device/GetGeoLocation_auto.go delete mode 100644 sdk/device/GetHostname_auto.go delete mode 100644 sdk/device/GetIPAddressFilter_auto.go delete mode 100644 sdk/device/GetNTP_auto.go delete mode 100644 sdk/device/GetNetworkDefaultGateway_auto.go delete mode 100644 sdk/device/GetNetworkInterfaces_auto.go delete mode 100644 sdk/device/GetNetworkProtocols_auto.go delete mode 100644 sdk/device/GetPkcs10Request_auto.go delete mode 100644 sdk/device/GetRelayOutputs_auto.go delete mode 100644 sdk/device/GetRemoteDiscoveryMode_auto.go delete mode 100644 sdk/device/GetRemoteUser_auto.go delete mode 100644 sdk/device/GetScopes_auto.go delete mode 100644 sdk/device/GetServiceCapabilities_auto.go delete mode 100644 sdk/device/GetServices_auto.go delete mode 100644 sdk/device/GetStorageConfiguration_auto.go delete mode 100644 sdk/device/GetStorageConfigurations_auto.go delete mode 100644 sdk/device/GetSystemBackup_auto.go delete mode 100644 sdk/device/GetSystemDateAndTime_auto.go delete mode 100644 sdk/device/GetSystemLog_auto.go delete mode 100644 sdk/device/GetSystemSupportInformation_auto.go delete mode 100644 sdk/device/GetSystemUris_auto.go delete mode 100644 sdk/device/GetUsers_auto.go delete mode 100644 sdk/device/GetWsdlUrl_auto.go delete mode 100644 sdk/device/GetZeroConfiguration_auto.go delete mode 100644 sdk/device/LoadCACertificates_auto.go delete mode 100644 sdk/device/LoadCertificateWithPrivateKey_auto.go delete mode 100644 sdk/device/LoadCertificates_auto.go delete mode 100644 sdk/device/RemoveIPAddressFilter_auto.go delete mode 100644 sdk/device/RemoveScopes_auto.go delete mode 100644 sdk/device/RestoreSystem_auto.go delete mode 100644 sdk/device/ScanAvailableDot11Networks_auto.go delete mode 100644 sdk/device/SendAuxiliaryCommand_auto.go delete mode 100644 sdk/device/SetAccessPolicy_auto.go delete mode 100644 sdk/device/SetCertificatesStatus_auto.go delete mode 100644 sdk/device/SetClientCertificateMode_auto.go delete mode 100644 sdk/device/SetDNS_auto.go delete mode 100644 sdk/device/SetDiscoveryMode_auto.go delete mode 100644 sdk/device/SetDot1XConfiguration_auto.go delete mode 100644 sdk/device/SetDynamicDNS_auto.go delete mode 100644 sdk/device/SetGeoLocation_auto.go delete mode 100644 sdk/device/SetHostnameFromDHCP_auto.go delete mode 100644 sdk/device/SetHostname_auto.go delete mode 100644 sdk/device/SetIPAddressFilter_auto.go delete mode 100644 sdk/device/SetNTP_auto.go delete mode 100644 sdk/device/SetNetworkDefaultGateway_auto.go delete mode 100644 sdk/device/SetNetworkInterfaces_auto.go delete mode 100644 sdk/device/SetNetworkProtocols_auto.go delete mode 100644 sdk/device/SetRelayOutputSettings_auto.go delete mode 100644 sdk/device/SetRelayOutputState_auto.go delete mode 100644 sdk/device/SetRemoteDiscoveryMode_auto.go delete mode 100644 sdk/device/SetRemoteUser_auto.go delete mode 100644 sdk/device/SetScopes_auto.go delete mode 100644 sdk/device/SetStorageConfiguration_auto.go delete mode 100644 sdk/device/SetSystemDateAndTime_auto.go delete mode 100644 sdk/device/SetSystemFactoryDefault_auto.go delete mode 100644 sdk/device/SetUser_auto.go delete mode 100644 sdk/device/SetZeroConfiguration_auto.go delete mode 100644 sdk/device/StartFirmwareUpgrade_auto.go delete mode 100644 sdk/device/StartSystemRestore_auto.go delete mode 100644 sdk/device/SystemReboot_auto.go delete mode 100644 sdk/device/UpgradeSystemFirmware_auto.go delete mode 100644 sdk/device/device.go delete mode 100644 sdk/event/CreatePullPointSubscription_auto.go delete mode 100644 sdk/event/GetEventProperties_auto.go delete mode 100644 sdk/event/GetServiceCapabilities_auto.go delete mode 100644 sdk/event/Subscribe_auto.go delete mode 100644 sdk/event/Unsubscribe_auto.go delete mode 100644 sdk/media/AddAudioDecoderConfiguration_auto.go delete mode 100644 sdk/media/AddAudioEncoderConfiguration_auto.go delete mode 100644 sdk/media/AddAudioOutputConfiguration_auto.go delete mode 100644 sdk/media/AddAudioSourceConfiguration_auto.go delete mode 100644 sdk/media/AddMetadataConfiguration_auto.go delete mode 100644 sdk/media/AddPTZConfiguration_auto.go delete mode 100644 sdk/media/AddVideoAnalyticsConfiguration_auto.go delete mode 100644 sdk/media/AddVideoEncoderConfiguration_auto.go delete mode 100644 sdk/media/AddVideoSourceConfiguration_auto.go delete mode 100644 sdk/media/CreateOSD_auto.go delete mode 100644 sdk/media/CreateProfile_auto.go delete mode 100644 sdk/media/DeleteOSD_auto.go delete mode 100644 sdk/media/DeleteProfile_auto.go delete mode 100644 sdk/media/GetAudioDecoderConfigurationOptions_auto.go delete mode 100644 sdk/media/GetAudioDecoderConfiguration_auto.go delete mode 100644 sdk/media/GetAudioDecoderConfigurations_auto.go delete mode 100644 sdk/media/GetAudioEncoderConfigurationOptions_auto.go delete mode 100644 sdk/media/GetAudioEncoderConfiguration_auto.go delete mode 100644 sdk/media/GetAudioEncoderConfigurations_auto.go delete mode 100644 sdk/media/GetAudioOutputConfigurationOptions_auto.go delete mode 100644 sdk/media/GetAudioOutputConfiguration_auto.go delete mode 100644 sdk/media/GetAudioOutputConfigurations_auto.go delete mode 100644 sdk/media/GetAudioOutputs_auto.go delete mode 100644 sdk/media/GetAudioSourceConfigurationOptions_auto.go delete mode 100644 sdk/media/GetAudioSourceConfiguration_auto.go delete mode 100644 sdk/media/GetAudioSourceConfigurations_auto.go delete mode 100644 sdk/media/GetAudioSources_auto.go delete mode 100644 sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleAudioOutputConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleAudioSourceConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleMetadataConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go delete mode 100644 sdk/media/GetCompatibleVideoSourceConfigurations_auto.go delete mode 100644 sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go delete mode 100644 sdk/media/GetMetadataConfigurationOptions_auto.go delete mode 100644 sdk/media/GetMetadataConfiguration_auto.go delete mode 100644 sdk/media/GetMetadataConfigurations_auto.go delete mode 100644 sdk/media/GetOSDOptions_auto.go delete mode 100644 sdk/media/GetOSD_auto.go delete mode 100644 sdk/media/GetOSDs_auto.go delete mode 100644 sdk/media/GetProfile_auto.go delete mode 100644 sdk/media/GetProfiles_auto.go delete mode 100644 sdk/media/GetServiceCapabilities_auto.go delete mode 100644 sdk/media/GetSnapshotUri_auto.go delete mode 100644 sdk/media/GetStreamUri_auto.go delete mode 100644 sdk/media/GetVideoAnalyticsConfiguration_auto.go delete mode 100644 sdk/media/GetVideoAnalyticsConfigurations_auto.go delete mode 100644 sdk/media/GetVideoEncoderConfigurationOptions_auto.go delete mode 100644 sdk/media/GetVideoEncoderConfiguration_auto.go delete mode 100644 sdk/media/GetVideoEncoderConfigurations_auto.go delete mode 100644 sdk/media/GetVideoSourceConfigurationOptions_auto.go delete mode 100644 sdk/media/GetVideoSourceConfiguration_auto.go delete mode 100644 sdk/media/GetVideoSourceConfigurations_auto.go delete mode 100644 sdk/media/GetVideoSourceModes_auto.go delete mode 100644 sdk/media/GetVideoSources_auto.go delete mode 100644 sdk/media/RemoveAudioDecoderConfiguration_auto.go delete mode 100644 sdk/media/RemoveAudioEncoderConfiguration_auto.go delete mode 100644 sdk/media/RemoveAudioOutputConfiguration_auto.go delete mode 100644 sdk/media/RemoveAudioSourceConfiguration_auto.go delete mode 100644 sdk/media/RemoveMetadataConfiguration_auto.go delete mode 100644 sdk/media/RemovePTZConfiguration_auto.go delete mode 100644 sdk/media/RemoveVideoAnalyticsConfiguration_auto.go delete mode 100644 sdk/media/RemoveVideoEncoderConfiguration_auto.go delete mode 100644 sdk/media/RemoveVideoSourceConfiguration_auto.go delete mode 100644 sdk/media/SetAudioDecoderConfiguration_auto.go delete mode 100644 sdk/media/SetAudioEncoderConfiguration_auto.go delete mode 100644 sdk/media/SetAudioOutputConfiguration_auto.go delete mode 100644 sdk/media/SetAudioSourceConfiguration_auto.go delete mode 100644 sdk/media/SetMetadataConfiguration_auto.go delete mode 100644 sdk/media/SetOSD_auto.go delete mode 100644 sdk/media/SetSynchronizationPoint_auto.go delete mode 100644 sdk/media/SetVideoAnalyticsConfiguration_auto.go delete mode 100644 sdk/media/SetVideoEncoderConfiguration_auto.go delete mode 100644 sdk/media/SetVideoSourceConfiguration_auto.go delete mode 100644 sdk/media/SetVideoSourceMode_auto.go delete mode 100644 sdk/media/StartMulticastStreaming_auto.go delete mode 100644 sdk/media/StopMulticastStreaming_auto.go delete mode 100644 sdk/media/media.go delete mode 100644 sdk/ptz/AbsoluteMove_auto.go delete mode 100644 sdk/ptz/ContinuousMove_auto.go delete mode 100644 sdk/ptz/CreatePresetTour_auto.go delete mode 100644 sdk/ptz/GeoMove_auto.go delete mode 100644 sdk/ptz/GetCompatibleConfigurations_auto.go delete mode 100644 sdk/ptz/GetConfigurationOptions_auto.go delete mode 100644 sdk/ptz/GetConfiguration_auto.go delete mode 100644 sdk/ptz/GetConfigurations_auto.go delete mode 100644 sdk/ptz/GetNode_auto.go delete mode 100644 sdk/ptz/GetNodes_auto.go delete mode 100644 sdk/ptz/GetPresetTourOptions_auto.go delete mode 100644 sdk/ptz/GetPresetTour_auto.go delete mode 100644 sdk/ptz/GetPresetTours_auto.go delete mode 100644 sdk/ptz/GetPresets_auto.go delete mode 100644 sdk/ptz/GetServiceCapabilities_auto.go delete mode 100644 sdk/ptz/GetStatus_auto.go delete mode 100644 sdk/ptz/GotoHomePosition_auto.go delete mode 100644 sdk/ptz/GotoPreset_auto.go delete mode 100644 sdk/ptz/ModifyPresetTour_auto.go delete mode 100644 sdk/ptz/OperatePresetTour_auto.go delete mode 100644 sdk/ptz/RelativeMove_auto.go delete mode 100644 sdk/ptz/RemovePresetTour_auto.go delete mode 100644 sdk/ptz/RemovePreset_auto.go delete mode 100644 sdk/ptz/SendAuxiliaryCommand_auto.go delete mode 100644 sdk/ptz/SetConfiguration_auto.go delete mode 100644 sdk/ptz/SetHomePosition_auto.go delete mode 100644 sdk/ptz/SetPreset_auto.go delete mode 100644 sdk/ptz/Stop_auto.go delete mode 100644 sdk/ptz/ptz.go delete mode 100644 sdk/sdk.go create mode 100644 ws-discovery/networking_test.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1e275fa --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + # Maintain dependencies for Go modules + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..72bb361 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea/ +.vscode/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index ce0aa0d..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/onvif.iml b/.idea/onvif.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/onvif.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 7746fc4..d58c7a7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,9 @@ "go.testTimeout": "10s", "go.formatTool": "goimports", "cSpell.allowCompoundWords": true, - "editor.codeActionsOnSave": {"source.organizeImports": true}, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, "cSpell.words": [ "wsdiscovery" ] diff --git a/Device.go b/Device.go index d88f548..ec4c258 100644 --- a/Device.go +++ b/Device.go @@ -1,8 +1,11 @@ package onvif import ( + "bytes" + "encoding/json" "encoding/xml" "errors" + "fmt" "io/ioutil" "net/http" "net/url" @@ -10,11 +13,11 @@ import ( "strconv" "strings" + "github.com/kerberos-io/onvif/xsd/onvif" + "github.com/beevik/etree" "github.com/kerberos-io/onvif/device" "github.com/kerberos-io/onvif/gosoap" - "github.com/kerberos-io/onvif/networking" - wsdiscovery "github.com/kerberos-io/onvif/ws-discovery" ) // Xlmns XML Scheam @@ -22,6 +25,7 @@ var Xlmns = map[string]string{ "onvif": "http://www.onvif.org/ver10/schema", "tds": "http://www.onvif.org/ver10/device/wsdl", "trt": "http://www.onvif.org/ver10/media/wsdl", + "tr2": "http://www.onvif.org/ver20/media/wsdl", "tev": "http://www.onvif.org/ver10/events/wsdl", "tptz": "http://www.onvif.org/ver20/ptz/wsdl", "timg": "http://www.onvif.org/ver20/imaging/wsdl", @@ -34,6 +38,9 @@ var Xlmns = map[string]string{ "wsntw": "http://docs.oasis-open.org/wsn/bw-2", "wsrf-rw": "http://docs.oasis-open.org/wsrf/rw-2", "wsaw": "http://www.w3.org/2006/05/addressing/wsdl", + "tt": "http://www.onvif.org/ver10/recording/wsdl", + "wsse": "http://docs.oasis-open.org/wss/2004/01/oasis-200401", + "wsu": "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", } // DeviceType alias for int @@ -45,6 +52,8 @@ const ( NVS NVA NVT + + ContentType = "Content-Type" ) func (devType DeviceType) String() string { @@ -65,6 +74,7 @@ func (devType DeviceType) String() string { // DeviceInfo struct contains general information about ONVIF device type DeviceInfo struct { + Name string Manufacturer string Model string FirmwareVersion string @@ -76,16 +86,19 @@ type DeviceInfo struct { // struct represents an abstract ONVIF device. // It contains methods, which helps to communicate with ONVIF device type Device struct { - params DeviceParams - endpoints map[string]string - info DeviceInfo + params DeviceParams + endpoints map[string]string + info DeviceInfo + digestClient *DigestClient } type DeviceParams struct { - Xaddr string - Username string - Password string - HttpClient *http.Client + Xaddr string + EndpointRefAddress string + Username string + Password string + HttpClient *http.Client + AuthMode string } // GetServices return available endpoints @@ -98,6 +111,34 @@ func (dev *Device) GetDeviceInfo() DeviceInfo { return dev.info } +// SetDeviceInfoFromScopes goes through the scopes and sets the device info fields for supported categories (currently name and hardware). +// See 7.3.2.2 Scopes in the ONVIF Core Specification (https://www.onvif.org/specs/core/ONVIF-Core-Specification.pdf). +func (dev *Device) SetDeviceInfoFromScopes(scopes []string) { + newInfo := dev.info + supportedScopes := []struct { + category string + setField func(s string) + }{ + {category: "name", setField: func(s string) { newInfo.Name = s }}, + {category: "hardware", setField: func(s string) { newInfo.Model = s }}, + } + + for _, s := range scopes { + for _, supp := range supportedScopes { + fullScope := fmt.Sprintf("onvif://www.onvif.org/%s/", supp.category) + scopeValue, matchesScope := strings.CutPrefix(s, fullScope) + if matchesScope { + unescaped, err := url.QueryUnescape(scopeValue) + if err != nil { + continue + } + supp.setField(unescaped) + } + } + } + dev.info = newInfo +} + func readResponse(resp *http.Response) string { b, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -106,58 +147,24 @@ func readResponse(resp *http.Response) string { return string(b) } -// GetAvailableDevicesAtSpecificEthernetInterface ... -func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) ([]Device, error) { - // Call a ws-discovery Probe Message to Discover NVT type Devices - devices, err := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) - if err != nil { - return nil, err - } - - nvtDevicesSeen := make(map[string]bool) - nvtDevices := make([]Device, 0) - - for _, j := range devices { - doc := etree.NewDocument() - if err := doc.ReadFromString(j); err != nil { - return nil, err - } - - for _, xaddr := range doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") { - xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] - if !nvtDevicesSeen[xaddr] { - dev, err := NewDevice(DeviceParams{Xaddr: strings.Split(xaddr, " ")[0]}) - if err != nil { - // TODO(jfsmig) print a warning - } else { - nvtDevicesSeen[xaddr] = true - nvtDevices = append(nvtDevices, *dev) - } - } - } - } - - return nvtDevices, nil -} - -func (dev *Device) getSupportedServices(data []byte) error { +func (dev *Device) getSupportedServices(resp *http.Response) { doc := etree.NewDocument() + + data, _ := ioutil.ReadAll(resp.Body) + if err := doc.ReadFromBytes(data); err != nil { //log.Println(err.Error()) - return err + return } - services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/*/XAddr") for _, j := range services { dev.addEndpoint(j.Parent().Tag, j.Text()) } - extension_services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/Extension/*/XAddr") - for _, j := range extension_services { + extensionServices := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/Extension/*/XAddr") + for _, j := range extensionServices { dev.addEndpoint(j.Parent().Tag, j.Text()) } - - return nil } // NewDevice function construct a ONVIF Device entity @@ -170,26 +177,17 @@ func NewDevice(params DeviceParams) (*Device, error) { if dev.params.HttpClient == nil { dev.params.HttpClient = new(http.Client) } + dev.digestClient = NewDigestClient(dev.params.HttpClient, dev.params.Username, dev.params.Password) - getCapabilities := device.GetCapabilities{Category: "All"} + getCapabilities := device.GetCapabilities{Category: []onvif.CapabilityCategory{"All"}} resp, err := dev.CallMethod(getCapabilities) - var b []byte - if resp != nil { - b, err = ioutil.ReadAll(resp.Body) - resp.Body.Close() - } - if err != nil || resp.StatusCode != http.StatusOK { return nil, errors.New("camera is not available at " + dev.params.Xaddr + " or it does not support ONVIF services") } - err = dev.getSupportedServices(b) - if err != nil { - return nil, err - } - + dev.getSupportedServices(resp) return dev, nil } @@ -205,6 +203,11 @@ func (dev *Device) addEndpoint(Key, Value string) { } dev.endpoints[lowCaseKey] = Value + + if lowCaseKey == strings.ToLower(MediaWebService) { + // Media2 uses the same endpoint but different XML name space + dev.endpoints[strings.ToLower(Media2WebService)] = Value + } } // GetEndpoint returns specific ONVIF service endpoint address @@ -212,7 +215,7 @@ func (dev *Device) GetEndpoint(name string) string { return dev.endpoints[name] } -func (dev Device) buildMethodSOAP(msg string) (gosoap.SoapMessage, error) { +func (dev *Device) buildMethodSOAP(msg string) (gosoap.SoapMessage, error) { doc := etree.NewDocument() if err := doc.ReadFromString(msg); err != nil { //log.Println("Got error") @@ -228,7 +231,7 @@ func (dev Device) buildMethodSOAP(msg string) (gosoap.SoapMessage, error) { } // getEndpoint functions get the target service endpoint in a better way -func (dev Device) getEndpoint(endpoint string) (string, error) { +func (dev *Device) getEndpoint(endpoint string) (string, error) { // common condition, endpointMark in map we use this. if endpointURL, bFound := dev.endpoints[endpoint]; bFound { @@ -250,7 +253,7 @@ func (dev Device) getEndpoint(endpoint string) (string, error) { // CallMethod functions call an method, defined struct. // You should use Authenticate method to call authorized requests. -func (dev Device) CallMethod(method interface{}) (*http.Response, error) { +func (dev *Device) CallMethod(method interface{}) (*http.Response, error) { pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/") pkg := strings.ToLower(pkgPath[len(pkgPath)-1]) @@ -258,28 +261,156 @@ func (dev Device) CallMethod(method interface{}) (*http.Response, error) { if err != nil { return nil, err } - return dev.callMethodDo(endpoint, method) + requestBody, err := xml.Marshal(method) + if err != nil { + return nil, err + } + return dev.SendSoap(endpoint, string(requestBody)) } -// CallMethod functions call an method, defined struct with authentication data -func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) { - output, err := xml.MarshalIndent(method, " ", " ") - if err != nil { - return nil, err - } +func (dev *Device) GetDeviceParams() DeviceParams { + return dev.params +} - soap, err := dev.buildMethodSOAP(string(output)) - if err != nil { - return nil, err - } +func (dev *Device) GetEndpointByRequestStruct(requestStruct interface{}) (string, error) { + pkgPath := strings.Split(reflect.TypeOf(requestStruct).Elem().PkgPath(), "/") + pkg := strings.ToLower(pkgPath[len(pkgPath)-1]) + endpoint, err := dev.getEndpoint(pkg) + if err != nil { + return "", err + } + return endpoint, err +} + +func (dev *Device) SendSoap(endpoint string, xmlRequestBody string) (resp *http.Response, err error) { + soap := gosoap.NewEmptySOAP() + soap.AddStringBodyContent(xmlRequestBody) soap.AddRootNamespaces(Xlmns) - soap.AddAction() - - //Auth Handling - if dev.params.Username != "" && dev.params.Password != "" { + if dev.params.AuthMode == UsernameTokenAuth || dev.params.AuthMode == Both { soap.AddWSSecurity(dev.params.Username, dev.params.Password) } - return networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) + if dev.params.AuthMode == DigestAuth || dev.params.AuthMode == Both { + resp, err = dev.digestClient.Do(http.MethodPost, endpoint, soap.String()) + } else { + var req *http.Request + req, err = createHttpRequest(http.MethodPost, endpoint, soap.String()) + if err != nil { + return nil, err + } + resp, err = dev.params.HttpClient.Do(req) + } + return resp, err +} + +func createHttpRequest(httpMethod string, endpoint string, soap string) (req *http.Request, err error) { + req, err = http.NewRequest(httpMethod, endpoint, bytes.NewBufferString(soap)) + if err != nil { + return nil, err + } + req.Header.Set(ContentType, "application/soap+xml; charset=utf-8") + return req, nil +} + +func (dev *Device) CallOnvifFunction(serviceName, functionName string, data []byte) (interface{}, error) { + function, err := FunctionByServiceAndFunctionName(serviceName, functionName) + if err != nil { + return nil, err + } + request, err := createRequest(function, data) + if err != nil { + return nil, fmt.Errorf("fail to create '%s' request for the web service '%s', %v", functionName, serviceName, err) + } + + endpoint, err := dev.GetEndpointByRequestStruct(request) + if err != nil { + return nil, err + } + + requestBody, err := xml.Marshal(request) + if err != nil { + return nil, err + } + xmlRequestBody := string(requestBody) + + servResp, err := dev.SendSoap(endpoint, xmlRequestBody) + if err != nil { + return nil, fmt.Errorf("fail to send the '%s' request for the web service '%s', %v", functionName, serviceName, err) + } + defer servResp.Body.Close() + + rsp, err := ioutil.ReadAll(servResp.Body) + if err != nil { + return nil, err + } + + responseEnvelope, err := createResponse(function, rsp) + if err != nil { + return nil, fmt.Errorf("fail to create '%s' response for the web service '%s', %v", functionName, serviceName, err) + } + + if servResp.StatusCode == http.StatusUnauthorized { + return nil, fmt.Errorf("fail to verify the authentication for the function '%s' of web service '%s'. Onvif error: %s", + functionName, serviceName, responseEnvelope.Body.Fault.String()) + } else if servResp.StatusCode == http.StatusBadRequest { + return nil, fmt.Errorf("invalid request for the function '%s' of web service '%s'. Onvif error: %s", + functionName, serviceName, responseEnvelope.Body.Fault.String()) + } else if servResp.StatusCode > http.StatusNoContent { + return nil, fmt.Errorf("fail to execute the request for the function '%s' of web service '%s'. Onvif error: %s", + functionName, serviceName, responseEnvelope.Body.Fault.String()) + } + return responseEnvelope.Body.Content, nil +} + +func createRequest(function Function, data []byte) (interface{}, error) { + request := function.Request() + if len(data) > 0 { + err := json.Unmarshal(data, request) + if err != nil { + return nil, err + } + } + return request, nil +} + +func createResponse(function Function, data []byte) (*gosoap.SOAPEnvelope, error) { + response := function.Response() + responseEnvelope := gosoap.NewSOAPEnvelope(response) + err := xml.Unmarshal(data, responseEnvelope) + if err != nil { + return nil, err + } + return responseEnvelope, nil +} + +// SendGetSnapshotRequest sends the Get request to retrieve the snapshot from the Onvif camera +// The parameter url is come from the "GetSnapshotURI" command. +func (dev *Device) SendGetSnapshotRequest(url string) (resp *http.Response, err error) { + soap := gosoap.NewEmptySOAP() + soap.AddRootNamespaces(Xlmns) + if dev.params.AuthMode == UsernameTokenAuth { + soap.AddWSSecurity(dev.params.Username, dev.params.Password) + var req *http.Request + req, err = createHttpRequest(http.MethodGet, url, soap.String()) + if err != nil { + return nil, err + } + // Basic auth might work for some camera + req.SetBasicAuth(dev.params.Username, dev.params.Password) + resp, err = dev.params.HttpClient.Do(req) + + } else if dev.params.AuthMode == DigestAuth || dev.params.AuthMode == Both { + soap.AddWSSecurity(dev.params.Username, dev.params.Password) + resp, err = dev.digestClient.Do(http.MethodGet, url, soap.String()) + + } else { + var req *http.Request + req, err = createHttpRequest(http.MethodGet, url, soap.String()) + if err != nil { + return nil, err + } + resp, err = dev.params.HttpClient.Do(req) + } + return resp, err } diff --git a/Device_test.go b/Device_test.go new file mode 100644 index 0000000..f8bfe04 --- /dev/null +++ b/Device_test.go @@ -0,0 +1,24 @@ +package onvif + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDevice_SetDeviceInfoFromScopes(t *testing.T) { + const ( + name = "DeviceName" + hardware = "M9000" + ) + scopes := []string{ + "onvif://www.onvif.org/Profile/Streaming", + "onvif://www.onvif.org/SomethingElse/value", + "onvif://www.onvif.org/name/" + name, + "onvif://www.onvif.org/hardware/" + hardware, + } + device := Device{} + device.SetDeviceInfoFromScopes(scopes) + assert.Equal(t, device.info.Name, name) + assert.Equal(t, device.info.Model, hardware) +} diff --git a/Imaging/types.go b/Imaging/types.go index cf0779a..adcd532 100644 --- a/Imaging/types.go +++ b/Imaging/types.go @@ -1,5 +1,7 @@ package imaging +//go:generate python3 ../python/gen_commands.py + import ( "github.com/kerberos-io/onvif/xsd" "github.com/kerberos-io/onvif/xsd/onvif" @@ -9,11 +11,19 @@ type GetServiceCapabilities struct { XMLName string `xml:"timg:GetServiceCapabilities"` } +// todo: fill in response type +type GetServiceCapabilitiesResponse struct { +} + type GetImagingSettings struct { XMLName string `xml:"timg:GetImagingSettings"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +type GetImagingSettingsResponse struct { + ImagingSettings onvif.ImagingSettings20 `xml:"timg:ImagingSettings"` +} + type SetImagingSettings struct { XMLName string `xml:"timg:SetImagingSettings"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` @@ -21,44 +31,78 @@ type SetImagingSettings struct { ForcePersistence xsd.Boolean `xml:"timg:ForcePersistence"` } +type SetImagingSettingsResponse struct { +} + type GetOptions struct { XMLName string `xml:"timg:GetOptions"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +// todo: fill in response type +type GetOptionsResponse struct { +} + type Move struct { XMLName string `xml:"timg:Move"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` Focus onvif.FocusMove `xml:"timg:Focus"` } +// todo: fill in response type +type MoveResponse struct { +} + type GetMoveOptions struct { XMLName string `xml:"timg:GetMoveOptions"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +// todo: fill in response type +type GetMoveOptionsResponse struct { +} + type Stop struct { XMLName string `xml:"timg:Stop"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +// todo: fill in response type +type StopResponse struct { +} + type GetStatus struct { XMLName string `xml:"timg:GetStatus"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +// todo: fill in response type +type GetStatusResponse struct { +} + type GetPresets struct { XMLName string `xml:"timg:GetPresets"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +// todo: fill in response type +type GetPresetsResponse struct { +} + type GetCurrentPreset struct { XMLName string `xml:"timg:GetCurrentPreset"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` } +// todo: fill in response type +type GetCurrentPresetResponse struct { +} + type SetCurrentPreset struct { XMLName string `xml:"timg:SetCurrentPreset"` VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` PresetToken onvif.ReferenceToken `xml:"timg:PresetToken"` } + +type SetCurrentPresetResponse struct { +} diff --git a/README.md b/README.md index 232487e..be444f5 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ -# ONVIF protocol +# Onvif library -Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard. +Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard. -## Installation +## Overview -To install the library, use **go get**: - -```go -go get github.com/kerberos-io/onvif - -``` +This repository is forked from: [use-go/onvif](https://github.com/use-go/onvif) ## Supported services @@ -18,30 +13,27 @@ The following services are implemented: - Device - Media - PTZ -- Imaging - Event - Discovery -- Auth(More Options) -- Soap ## Using ### General concept -1. Connecting to the device -2. Authentication (if necessary) -3. Defining Data Types -4. Carrying out the required method +1) Connecting to the device +2) Authentication (if necessary) +3) Defining Data Types +4) Carrying out the required method #### Connecting to the device -If there is a device on the network at the address _192.168.13.42_, and its ONVIF services use the _1234_ port, then you can connect to the device in the following way: +If there is a device on the network at the address *192.168.13.42*, and its ONVIF services use the *1234* port, then you can connect to the device in the following way: ```go dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234"}) ``` -\*The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. **Usually this is 80 port.\*** +*The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. **Usually this is 80 port.*** #### Authentication @@ -75,7 +67,7 @@ The figure below shows that `GetServiceCapabilities` does not accept any argumen ![PTZ GetServiceCapabilities](docs/img/GetServiceCapabilities.png) -_Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package._ +*Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package.* An example of how to define the data type of the CreateUsers function in [Devicemgmt](https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl): @@ -98,6 +90,5 @@ device.Authenticate("username", "password") resp, err := dev.CallMethod(createUsers) ``` -## Great Thanks - -Enhanced and Improved from: [goonvif](https://github.com/yakovlevdmv/goonvif) +## Development +See [here](docs/Development.md) diff --git a/analytics/common.go b/analytics/common.go new file mode 100644 index 0000000..458ecb1 --- /dev/null +++ b/analytics/common.go @@ -0,0 +1,47 @@ +package analytics + +import "github.com/kerberos-io/onvif/xsd" + +type Parameters struct { + SimpleItemDescription []SimpleItemDescription `json:",omitempty"` + ElementItemDescription []ElementItemDescription `json:",omitempty"` + Extension *xsd.String `json:",omitempty"` +} + +type SimpleItemDescription struct { + Name string `json:",omitempty" xml:",attr"` + Type string `json:",omitempty" xml:",attr"` + Value string `json:",omitempty" xml:",attr"` +} + +type ElementItemDescription struct { + Name string `json:",omitempty" xml:",attr"` + Value string `json:",omitempty" xml:",attr"` +} + +type Messages struct { + IsProperty *xsd.Boolean `json:",omitempty" xml:",attr"` + Source *Source `json:",omitempty"` + Key *Key `json:",omitempty"` + Data *Data `json:",omitempty"` + Extension *xsd.String `json:",omitempty"` + ParentTopic *xsd.String `json:",omitempty"` +} + +type Source struct { + SimpleItemDescription []SimpleItemDescription `json:",omitempty"` + ElementItemDescription []ElementItemDescription `json:",omitempty"` + Extension *xsd.String `json:",omitempty"` +} + +type Key struct { + SimpleItemDescription []SimpleItemDescription `json:",omitempty"` + ElementItemDescription []ElementItemDescription `json:",omitempty"` + Extension *xsd.String `json:",omitempty"` +} + +type Data struct { + SimpleItemDescription []SimpleItemDescription `json:",omitempty"` + ElementItemDescription []ElementItemDescription `json:",omitempty"` + Extension *xsd.String `json:",omitempty"` +} diff --git a/analytics/function.go b/analytics/function.go new file mode 100644 index 0000000..23ca063 --- /dev/null +++ b/analytics/function.go @@ -0,0 +1,117 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package analytics + +type CreateAnalyticsModulesFunction struct{} + +func (_ *CreateAnalyticsModulesFunction) Request() interface{} { + return &CreateAnalyticsModules{} +} +func (_ *CreateAnalyticsModulesFunction) Response() interface{} { + return &CreateAnalyticsModulesResponse{} +} + +type CreateRulesFunction struct{} + +func (_ *CreateRulesFunction) Request() interface{} { + return &CreateRules{} +} +func (_ *CreateRulesFunction) Response() interface{} { + return &CreateRulesResponse{} +} + +type DeleteAnalyticsModulesFunction struct{} + +func (_ *DeleteAnalyticsModulesFunction) Request() interface{} { + return &DeleteAnalyticsModules{} +} +func (_ *DeleteAnalyticsModulesFunction) Response() interface{} { + return &DeleteAnalyticsModulesResponse{} +} + +type DeleteRulesFunction struct{} + +func (_ *DeleteRulesFunction) Request() interface{} { + return &DeleteRules{} +} +func (_ *DeleteRulesFunction) Response() interface{} { + return &DeleteRulesResponse{} +} + +type GetAnalyticsModuleOptionsFunction struct{} + +func (_ *GetAnalyticsModuleOptionsFunction) Request() interface{} { + return &GetAnalyticsModuleOptions{} +} +func (_ *GetAnalyticsModuleOptionsFunction) Response() interface{} { + return &GetAnalyticsModuleOptionsResponse{} +} + +type GetAnalyticsModulesFunction struct{} + +func (_ *GetAnalyticsModulesFunction) Request() interface{} { + return &GetAnalyticsModules{} +} +func (_ *GetAnalyticsModulesFunction) Response() interface{} { + return &GetAnalyticsModulesResponse{} +} + +type GetRuleOptionsFunction struct{} + +func (_ *GetRuleOptionsFunction) Request() interface{} { + return &GetRuleOptions{} +} +func (_ *GetRuleOptionsFunction) Response() interface{} { + return &GetRuleOptionsResponse{} +} + +type GetRulesFunction struct{} + +func (_ *GetRulesFunction) Request() interface{} { + return &GetRules{} +} +func (_ *GetRulesFunction) Response() interface{} { + return &GetRulesResponse{} +} + +type GetSupportedAnalyticsModulesFunction struct{} + +func (_ *GetSupportedAnalyticsModulesFunction) Request() interface{} { + return &GetSupportedAnalyticsModules{} +} +func (_ *GetSupportedAnalyticsModulesFunction) Response() interface{} { + return &GetSupportedAnalyticsModulesResponse{} +} + +type GetSupportedRulesFunction struct{} + +func (_ *GetSupportedRulesFunction) Request() interface{} { + return &GetSupportedRules{} +} +func (_ *GetSupportedRulesFunction) Response() interface{} { + return &GetSupportedRulesResponse{} +} + +type ModifyAnalyticsModulesFunction struct{} + +func (_ *ModifyAnalyticsModulesFunction) Request() interface{} { + return &ModifyAnalyticsModules{} +} +func (_ *ModifyAnalyticsModulesFunction) Response() interface{} { + return &ModifyAnalyticsModulesResponse{} +} + +type ModifyRulesFunction struct{} + +func (_ *ModifyRulesFunction) Request() interface{} { + return &ModifyRules{} +} +func (_ *ModifyRulesFunction) Response() interface{} { + return &ModifyRulesResponse{} +} diff --git a/analytics/types.go b/analytics/types.go index 2b9baf6..d915b4e 100644 --- a/analytics/types.go +++ b/analytics/types.go @@ -1,78 +1,217 @@ package analytics +//go:generate python3 ../python/gen_commands.py + import ( "github.com/kerberos-io/onvif/xsd" "github.com/kerberos-io/onvif/xsd/onvif" ) +// GetSupportedAnalyticsModules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.GetSupportedAnalyticsModules +type GetSupportedAnalyticsModules struct { + XMLName string `xml:"tan:GetSupportedAnalyticsModules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` +} + +type GetSupportedAnalyticsModulesResponse struct { + SupportedAnalyticsModules SupportedAnalyticsModules +} + +type SupportedAnalyticsModules struct { + Limit *xsd.Int `json:",omitempty"` + AnalyticsModuleContentSchemaLocation *xsd.String `json:",omitempty"` + AnalyticsModuleDescription []AnalyticsModuleDescription `json:",omitempty"` +} + +type AnalyticsModuleDescription struct { + Name string `xml:"Name,attr"` + Fixed bool `xml:"fixed,attr"` + MaxInstances int `xml:"maxInstances,attr"` + Parameters *Parameters `json:",omitempty"` + Messages *Messages `json:",omitempty"` +} + +// CreateAnalyticsModules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.CreateAnalyticsModules +type CreateAnalyticsModules struct { + XMLName string `xml:"tev:CreateAnalyticsModules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` + AnalyticsModule []onvif.ConfigRequest `xml:"tan:AnalyticsModule"` +} + +type CreateAnalyticsModulesResponse struct{} + +// DeleteAnalyticsModules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.DeleteAnalyticsModules +type DeleteAnalyticsModules struct { + XMLName string `xml:"tan:DeleteAnalyticsModules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` + AnalyticsModuleName []xsd.String `xml:"tan:AnalyticsModuleName"` +} + +type DeleteAnalyticsModulesResponse struct{} + +// GetAnalyticsModules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.GetAnalyticsModules +type GetAnalyticsModules struct { + XMLName string `xml:"tan:GetAnalyticsModules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` +} + +type GetAnalyticsModulesResponse struct { + AnalyticsModule []onvif.Config +} + +// GetAnalyticsModuleOptions and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.GetAnalyticsModuleOptions +type GetAnalyticsModuleOptions struct { + XMLName string `xml:"tan:GetAnalyticsModuleOptions"` + Type xsd.QName `xml:"tan:Type,omitempty"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` +} + +type GetAnalyticsModuleOptionsResponse struct { + Options []AnalyticsModuleOptions +} + +type AnalyticsModuleOptions struct { + RuleType string `json:",omitempty" xml:",attr"` + Name string `json:",omitempty" xml:",attr"` + Type string `json:",omitempty" xml:",attr"` + AnalyticsModule string `json:",omitempty" xml:",attr"` + IntRange *IntRange `json:",omitempty"` + StringItems *StringItems `json:",omitempty"` +} + +type IntRange struct { + Min int + Max int +} + +type StringItems struct { + Item []string +} + +// ModifyAnalyticsModules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.ModifyAnalyticsModules +type ModifyAnalyticsModules struct { + XMLName string `xml:"tan:ModifyAnalyticsModules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` + AnalyticsModule []onvif.ConfigRequest `xml:"tan:AnalyticsModule"` +} + +type ModifyAnalyticsModulesResponse struct{} + +// GetSupportedRules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.GetSupportedRules type GetSupportedRules struct { XMLName string `xml:"tan:GetSupportedRules"` ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` } -type CreateRules struct { - XMLName string `xml:"tan:CreateRules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` - Rule onvif.Config `xml:"tan:Rule"` +type GetSupportedRulesResponse struct { + SupportedRules SupportedRules } -type DeleteRules struct { - XMLName string `xml:"tan:DeleteRules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` - RuleName xsd.String `xml:"tan:RuleName"` +type SupportedRules struct { + Limit *xsd.Int `json:",omitempty"` + RuleContentSchemaLocation *xsd.String `json:",omitempty"` + RuleDescription []RuleDescription } +type RuleDescription struct { + Name *xsd.String `json:",omitempty" xml:",attr"` + Fixed *xsd.Boolean `json:",omitempty" xml:"fixed,attr"` + MaxInstances *xsd.Int `json:",omitempty" xml:"maxInstances,attr"` + Parameters Parameters + Messages Messages `json:",omitempty"` +} + +// GetRules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.GetRules type GetRules struct { XMLName string `xml:"tan:GetRules"` ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` } +type GetRulesResponse struct { + Rule []onvif.Config +} + +// CreateRules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.CreateRules +type CreateRules struct { + XMLName string `xml:"tan:CreateRules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` + Rule []onvif.ConfigRequest `xml:"tan:Rule"` +} + +type ItemListExtension xsd.AnyType + +type CreateRulesResponse struct{} + +// DeleteRules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.DeleteRules +type DeleteRules struct { + XMLName string `xml:"tan:DeleteRules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` + RuleName []xsd.String `xml:"tan:RuleName"` +} + +type DeleteRulesResponse struct{} + +// GetRuleOptions and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.GetRuleOptions type GetRuleOptions struct { XMLName string `xml:"tan:GetRuleOptions"` RuleType xsd.QName `xml:"tan:RuleType"` ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` } -type ModifyRules struct { - XMLName string `xml:"tan:ModifyRules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` - Rule onvif.Config `xml:"tan:Rule"` +type GetRuleOptionsResponse struct { + RuleOptions []RuleOptions } +type RuleOptions struct { + RuleType *xsd.String `json:",omitempty"` + Name *xsd.String `json:",omitempty" xml:",attr"` + Type *xsd.String `json:",omitempty" xml:",attr"` + MinOccurs *xsd.String `json:",omitempty" xml:"minOccurs,attr"` + MaxOccurs *xsd.String `json:",omitempty" xml:"maxOccurs,attr"` + AnalyticsModule *xsd.String `json:",omitempty"` + IntRange *IntRange `json:",omitempty"` + StringItems *StringItems `json:",omitempty"` + PolygonOptions *PolygonOptions `json:",omitempty"` + MotionRegionConfigOptions *MotionRegionConfigOptions `json:",omitempty"` + StringList *xsd.String `json:",omitempty"` +} + +type PolygonOptions struct { + VertexLimits VertexLimits +} + +type VertexLimits struct { + Min int + Max int +} + +type MotionRegionConfigOptions struct { + DisarmSupport bool + PolygonSupport bool + PolygonLimits VertexLimits +} + +// ModifyRules and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl#op.ModifyRules +type ModifyRules struct { + XMLName string `xml:"tan:ModifyRules"` + ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` + Rule []onvif.ConfigRequest `xml:"tan:Rule"` +} + +type ModifyRulesResponse struct{} + type GetServiceCapabilities struct { XMLName string `xml:"tan:GetServiceCapabilities"` } - -type GetSupportedAnalyticsModules struct { - XMLName string `xml:"tan:GetSupportedAnalyticsModules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` -} - -type GetAnalyticsModuleOptions struct { - XMLName string `xml:"tan:GetAnalyticsModuleOptions"` - Type xsd.QName `xml:"tan:Type"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` -} - -type CreateAnalyticsModules struct { - XMLName string `xml:"tev:CreateAnalyticsModules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` - AnalyticsModule onvif.Config `xml:"tan:AnalyticsModule"` -} - -type DeleteAnalyticsModules struct { - XMLName string `xml:"tan:DeleteAnalyticsModules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` - AnalyticsModuleName xsd.String `xml:"tan:AnalyticsModuleName"` -} - -type GetAnalyticsModules struct { - XMLName string `xml:"tan:GetAnalyticsModules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` -} - -type ModifyAnalyticsModules struct { - XMLName string `xml:"tan:ModifyAnalyticsModules"` - ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` - AnalyticsModule onvif.Config `xml:"tan:AnalyticsModule"` -} diff --git a/api/api.go b/api/api.go index af59126..ca0ac12 100644 --- a/api/api.go +++ b/api/api.go @@ -1,17 +1,14 @@ package api import ( + "errors" + "fmt" "io/ioutil" "net/http" - "os" "path" "reflect" "regexp" "strings" - "time" - - "github.com/juju/errors" - "github.com/rs/zerolog" "github.com/beevik/etree" "github.com/gin-gonic/gin" @@ -21,18 +18,6 @@ import ( wsdiscovery "github.com/kerberos-io/onvif/ws-discovery" ) -var ( - // LoggerContext is the builder of a zerolog.Logger that is exposed to the application so that - // options at the CLI might alter the formatting and the output of the logs. - LoggerContext = zerolog. - New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}). - With().Timestamp() - - // Logger is a zerolog logger, that can be safely used from any part of the application. - // It gathers the format and the output. - Logger = LoggerContext.Logger() -) - func RunApi() { router := gin.Default() @@ -47,7 +32,7 @@ func RunApi() { xaddr := c.GetHeader("xaddr") acceptedData, err := c.GetRawData() if err != nil { - Logger.Debug().Err(err).Msg("Failed to get rawx data") + fmt.Println(err) } message, err := callNecessaryMethod(serviceName, methodName, string(acceptedData), username, pass, xaddr) @@ -64,45 +49,46 @@ func RunApi() { interfaceName := context.GetHeader("interface") - devices, err := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:NetworkVideoTransmitter"}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) - if err != nil { - context.String(http.StatusInternalServerError, "error") - } else { - response := "[" + var response = "[" + // TODO: Handle this error. + devices, _ := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:NetworkVideoTransmitter"}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) - for _, j := range devices { - doc := etree.NewDocument() - if err := doc.ReadFromString(j); err != nil { - context.XML(http.StatusBadRequest, err.Error()) - } else { + for _, j := range devices { + doc := etree.NewDocument() + if err := doc.ReadFromString(j); err != nil { + context.XML(http.StatusBadRequest, err.Error()) + } else { - endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") - scopes := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/Scopes") + endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") + scopes := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/Scopes") - flag := false + flag := false - for _, xaddr := range endpoints { - xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] - if strings.Contains(response, xaddr) { - flag = true - break - } - response += "{" - response += `"url":"` + xaddr + `",` - } - if flag { + for _, xaddr := range endpoints { + xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] + if strings.Contains(response, xaddr) { + flag = true break } - for _, scope := range scopes { - re := regexp.MustCompile(`onvif:\/\/www\.onvif\.org\/name\/[A-Za-z0-9-]+`) - match := re.FindStringSubmatch(scope.Text()) - response += `"name":"` + path.Base(match[0]) + `"` - } - response += "}," + response += "{" + response += `"url":"` + xaddr + `",` } + if flag { + break + } + for _, scope := range scopes { + re := regexp.MustCompile(`onvif:\/\/www\.onvif\.org\/name\/[A-Za-z0-9-]+`) + match := re.FindStringSubmatch(scope.Text()) + response += `"name":"` + path.Base(match[0]) + `"` + } + response += "}," + } - response = strings.TrimRight(response, ",") - response += "]" + + } + response = strings.TrimRight(response, ",") + response += "]" + if response != "" { context.String(http.StatusOK, response) } }) @@ -110,6 +96,25 @@ func RunApi() { router.Run() } +//func soapHandling(tp interface{}, tags* map[string]string) { +// ifaceValue := reflect.ValueOf(tp).Elem() +// typeOfStruct := ifaceValue.Type() +// if ifaceValue.Kind() != reflect.Struct { +// return +// } +// for i := 0; i < ifaceValue.NumField(); i++ { +// field := ifaceValue.Field(i) +// tg, err := typeOfStruct.FieldByName(typeOfStruct.Field(i).Name) +// if err == false { +// fmt.Println(err) +// } +// (*tags)[typeOfStruct.Field(i).Name] = string(tg.Tag) +// +// subStruct := reflect.New(reflect.TypeOf( field.Interface() )) +// soapHandling(subStruct.Interface(), tags) +// } +//} + func callNecessaryMethod(serviceName, methodName, acceptedData, username, password, xaddr string) (string, error) { var methodStruct interface{} var err error @@ -125,17 +130,17 @@ func callNecessaryMethod(serviceName, methodName, acceptedData, username, passwo return "", errors.New("there is no such service") } if err != nil { //done - return "", errors.Annotate(err, "getStructByName") + return "", err } resp, err := xmlAnalize(methodStruct, &acceptedData) if err != nil { - return "", errors.Annotate(err, "xmlAnalize") + return "", err } endpoint, err := getEndpoint(serviceName, xaddr) if err != nil { - return "", errors.Annotate(err, "getEndpoint") + return "", err } soap := gosoap.NewEmptySOAP() @@ -145,23 +150,21 @@ func callNecessaryMethod(serviceName, methodName, acceptedData, username, passwo servResp, err := networking.SendSoap(new(http.Client), endpoint, soap.String()) if err != nil { - return "", errors.Annotate(err, "SendSoap") + return "", err } rsp, err := ioutil.ReadAll(servResp.Body) if err != nil { - return "", errors.Annotate(err, "ReadAll") + return "", err } - servResp.Body.Close() - return string(rsp), nil } func getEndpoint(service, xaddr string) (string, error) { dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: xaddr}) if err != nil { - return "", errors.Annotate(err, "NewDevice") + return "", err } pkg := strings.ToLower(service) @@ -191,7 +194,7 @@ func xmlAnalize(methodStruct interface{}, acceptedData *string) (*string, error) doc := etree.NewDocument() if err := doc.ReadFromString(*acceptedData); err != nil { - return nil, errors.Annotate(err, "readFromString") + return nil, err } etr := doc.FindElements("./*") xmlUnmarshal(etr, &testunMarshal, &mas) @@ -205,7 +208,7 @@ func xmlAnalize(methodStruct interface{}, acceptedData *string) (*string, error) lst := (testunMarshal)[lstIndex] elemName, attr, value, err := xmlMaker(&lst, &test, lstIndex) if err != nil { - return nil, errors.Annotate(err, "xmlMarker") + return nil, err } if mas[lstIndex] == "Push" && lstIndex == 0 { //done @@ -249,10 +252,10 @@ func xmlAnalize(methodStruct interface{}, acceptedData *string) (*string, error) resp, err := document.WriteToString() if err != nil { - return nil, errors.Annotate(err, "writeToString") + return nil, err } - return &resp, nil + return &resp, err } func xmlMaker(lst *[]interface{}, tags *[]map[string]string, lstIndex int) (string, map[string]string, string, error) { @@ -271,13 +274,13 @@ func xmlMaker(lst *[]interface{}, tags *[]map[string]string, lstIndex int) (stri if index == 0 && lstIndex == 0 { res, err := xmlProcessing(tg["XMLName"]) if err != nil { - return "", nil, "", errors.Annotate(err, "xmlProcessing") + return "", nil, "", err } elemName = res } else if index == 0 { res, err := xmlProcessing(tg[conversion]) if err != nil { - return "", nil, "", errors.Annotate(err, "xmlProcessing") + return "", nil, "", err } elemName = res } else { @@ -312,6 +315,8 @@ func xmlProcessing(tg string) (string, error) { } else { return str[1][0:omitAttr], nil } + + return "", errors.New("something went wrong") } func mapProcessing(mapVar []map[string]string) []map[string]string { @@ -339,9 +344,9 @@ func soapHandling(tp interface{}, tags *[]map[string]string) { } for i := 0; i < s.NumField(); i++ { f := s.Field(i) - tmp, ok := typeOfT.FieldByName(typeOfT.Field(i).Name) - if !ok { - Logger.Debug().Str("field", typeOfT.Field(i).Name).Msg("reflection failed") + tmp, err := typeOfT.FieldByName(typeOfT.Field(i).Name) + if err == false { + fmt.Println(err) } *tags = append(*tags, map[string]string{typeOfT.Field(i).Name: string(tmp.Tag)}) subStruct := reflect.New(reflect.TypeOf(f.Interface())) diff --git a/cmd/camera-example/main.go b/cmd/camera-example/main.go new file mode 100644 index 0000000..c3eea63 --- /dev/null +++ b/cmd/camera-example/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "io/ioutil" + "net/http" + + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/gosoap" + "github.com/kerberos-io/onvif/networking" + + "github.com/gin-gonic/gin" +) + +func main() { + RunApi() +} + +func RunApi() { + router := gin.Default() + + router.POST("/:service/:function", func(c *gin.Context) { + c.Header("Access-Control-Allow-Origin", "*") + //c.Header("Access-Control-Allow-Headers", "access-control-allow-origin, access-control-allow-headers") + + serviceName := c.Param("service") + functionName := c.Param("function") + username := c.GetHeader("username") + pass := c.GetHeader("password") + xaddr := c.GetHeader("xaddr") + acceptedData, err := c.GetRawData() + if err != nil { + fmt.Println(err) + } + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: xaddr, + Username: username, + Password: pass, + }) + message, err := CallOnvifFunction(dev, serviceName, functionName, string(acceptedData)) + if err != nil { + c.XML(http.StatusBadRequest, err.Error()) + } else { + c.String(http.StatusOK, message) + } + }) + + router.Run() +} + +func CallOnvifFunction(dev *onvif.Device, serviceName, functionName, acceptedData string) (string, error) { + function, err := functionByServiceAndFunctionName(serviceName, functionName) + if err != nil { + return "", err + } + request := function.Request() + + if len(acceptedData) > 0 { + err = json.Unmarshal([]byte(acceptedData), request) + if err != nil { + return "", err + } + } + + requestBody, err := xml.Marshal(request) + if err != nil { + return "", err + } + + endpoint, err := dev.GetEndpointByRequestStruct(request) + if err != nil { + return "", err + } + + soap := gosoap.NewEmptySOAP() + soap.AddStringBodyContent(string(requestBody)) + soap.AddRootNamespaces(onvif.Xlmns) + soap.AddWSSecurity(dev.GetDeviceParams().Username, dev.GetDeviceParams().Password) + + servResp, err := networking.SendSoap(new(http.Client), endpoint, soap.String()) + if err != nil { + return "", err + } + + rsp, err := ioutil.ReadAll(servResp.Body) + if err != nil { + return "", err + } + + responseEnvelope := gosoap.NewSOAPEnvelope(function.Response()) + err = xml.Unmarshal(rsp, responseEnvelope) + if err != nil { + return "", err + } + + if responseEnvelope.Body.Fault != nil { + jsonData, err := json.Marshal(responseEnvelope.Body.Fault) + if err != nil { + return "", err + } + return string(jsonData), nil + } else { + jsonData, err := json.Marshal(responseEnvelope.Body.Content) + if err != nil { + return "", err + } + return string(jsonData), nil + } + +} + +func functionByServiceAndFunctionName(serviceName, functionName string) (onvif.Function, error) { + var function onvif.Function + var exist bool + switch serviceName { + case onvif.DeviceWebService: + function, exist = onvif.DeviceFunctionMap[functionName] + if !exist { + return nil, fmt.Errorf("the web service '%s'not support the function '%s'", serviceName, functionName) + } + case onvif.MediaWebService: + function, exist = onvif.MediaFunctionMap[functionName] + if !exist { + return nil, fmt.Errorf("the web service '%s' not support the function '%s'", serviceName, functionName) + } + default: + return nil, fmt.Errorf("not support the web service '%s'", serviceName) + } + return function, nil +} diff --git a/constant.go b/constant.go new file mode 100644 index 0000000..a638cd2 --- /dev/null +++ b/constant.go @@ -0,0 +1,9 @@ +package onvif + +// Onvif Auth Mode +const ( + DigestAuth = "digest" + UsernameTokenAuth = "usernametoken" + Both = "both" + NoAuth = "none" +) diff --git a/device/function.go b/device/function.go new file mode 100644 index 0000000..d018c68 --- /dev/null +++ b/device/function.go @@ -0,0 +1,819 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package device + +type AddIPAddressFilterFunction struct{} + +func (_ *AddIPAddressFilterFunction) Request() interface{} { + return &AddIPAddressFilter{} +} +func (_ *AddIPAddressFilterFunction) Response() interface{} { + return &AddIPAddressFilterResponse{} +} + +type AddScopesFunction struct{} + +func (_ *AddScopesFunction) Request() interface{} { + return &AddScopes{} +} +func (_ *AddScopesFunction) Response() interface{} { + return &AddScopesResponse{} +} + +type CreateCertificateFunction struct{} + +func (_ *CreateCertificateFunction) Request() interface{} { + return &CreateCertificate{} +} +func (_ *CreateCertificateFunction) Response() interface{} { + return &CreateCertificateResponse{} +} + +type CreateDot1XConfigurationFunction struct{} + +func (_ *CreateDot1XConfigurationFunction) Request() interface{} { + return &CreateDot1XConfiguration{} +} +func (_ *CreateDot1XConfigurationFunction) Response() interface{} { + return &CreateDot1XConfigurationResponse{} +} + +type CreateStorageConfigurationFunction struct{} + +func (_ *CreateStorageConfigurationFunction) Request() interface{} { + return &CreateStorageConfiguration{} +} +func (_ *CreateStorageConfigurationFunction) Response() interface{} { + return &CreateStorageConfigurationResponse{} +} + +type CreateUsersFunction struct{} + +func (_ *CreateUsersFunction) Request() interface{} { + return &CreateUsers{} +} +func (_ *CreateUsersFunction) Response() interface{} { + return &CreateUsersResponse{} +} + +type DeleteCertificatesFunction struct{} + +func (_ *DeleteCertificatesFunction) Request() interface{} { + return &DeleteCertificates{} +} +func (_ *DeleteCertificatesFunction) Response() interface{} { + return &DeleteCertificatesResponse{} +} + +type DeleteDot1XConfigurationFunction struct{} + +func (_ *DeleteDot1XConfigurationFunction) Request() interface{} { + return &DeleteDot1XConfiguration{} +} +func (_ *DeleteDot1XConfigurationFunction) Response() interface{} { + return &DeleteDot1XConfigurationResponse{} +} + +type DeleteGeoLocationFunction struct{} + +func (_ *DeleteGeoLocationFunction) Request() interface{} { + return &DeleteGeoLocation{} +} +func (_ *DeleteGeoLocationFunction) Response() interface{} { + return &DeleteGeoLocationResponse{} +} + +type DeleteStorageConfigurationFunction struct{} + +func (_ *DeleteStorageConfigurationFunction) Request() interface{} { + return &DeleteStorageConfiguration{} +} +func (_ *DeleteStorageConfigurationFunction) Response() interface{} { + return &DeleteStorageConfigurationResponse{} +} + +type DeleteUsersFunction struct{} + +func (_ *DeleteUsersFunction) Request() interface{} { + return &DeleteUsers{} +} +func (_ *DeleteUsersFunction) Response() interface{} { + return &DeleteUsersResponse{} +} + +type GetAccessPolicyFunction struct{} + +func (_ *GetAccessPolicyFunction) Request() interface{} { + return &GetAccessPolicy{} +} +func (_ *GetAccessPolicyFunction) Response() interface{} { + return &GetAccessPolicyResponse{} +} + +type GetCACertificatesFunction struct{} + +func (_ *GetCACertificatesFunction) Request() interface{} { + return &GetCACertificates{} +} +func (_ *GetCACertificatesFunction) Response() interface{} { + return &GetCACertificatesResponse{} +} + +type GetCapabilitiesFunction struct{} + +func (_ *GetCapabilitiesFunction) Request() interface{} { + return &GetCapabilities{} +} +func (_ *GetCapabilitiesFunction) Response() interface{} { + return &GetCapabilitiesResponse{} +} + +type GetCertificateInformationFunction struct{} + +func (_ *GetCertificateInformationFunction) Request() interface{} { + return &GetCertificateInformation{} +} +func (_ *GetCertificateInformationFunction) Response() interface{} { + return &GetCertificateInformationResponse{} +} + +type GetCertificatesFunction struct{} + +func (_ *GetCertificatesFunction) Request() interface{} { + return &GetCertificates{} +} +func (_ *GetCertificatesFunction) Response() interface{} { + return &GetCertificatesResponse{} +} + +type GetCertificatesStatusFunction struct{} + +func (_ *GetCertificatesStatusFunction) Request() interface{} { + return &GetCertificatesStatus{} +} +func (_ *GetCertificatesStatusFunction) Response() interface{} { + return &GetCertificatesStatusResponse{} +} + +type GetClientCertificateModeFunction struct{} + +func (_ *GetClientCertificateModeFunction) Request() interface{} { + return &GetClientCertificateMode{} +} +func (_ *GetClientCertificateModeFunction) Response() interface{} { + return &GetClientCertificateModeResponse{} +} + +type GetDNSFunction struct{} + +func (_ *GetDNSFunction) Request() interface{} { + return &GetDNS{} +} +func (_ *GetDNSFunction) Response() interface{} { + return &GetDNSResponse{} +} + +type GetDPAddressesFunction struct{} + +func (_ *GetDPAddressesFunction) Request() interface{} { + return &GetDPAddresses{} +} +func (_ *GetDPAddressesFunction) Response() interface{} { + return &GetDPAddressesResponse{} +} + +type GetDeviceInformationFunction struct{} + +func (_ *GetDeviceInformationFunction) Request() interface{} { + return &GetDeviceInformation{} +} +func (_ *GetDeviceInformationFunction) Response() interface{} { + return &GetDeviceInformationResponse{} +} + +type GetDiscoveryModeFunction struct{} + +func (_ *GetDiscoveryModeFunction) Request() interface{} { + return &GetDiscoveryMode{} +} +func (_ *GetDiscoveryModeFunction) Response() interface{} { + return &GetDiscoveryModeResponse{} +} + +type GetDot11CapabilitiesFunction struct{} + +func (_ *GetDot11CapabilitiesFunction) Request() interface{} { + return &GetDot11Capabilities{} +} +func (_ *GetDot11CapabilitiesFunction) Response() interface{} { + return &GetDot11CapabilitiesResponse{} +} + +type GetDot11StatusFunction struct{} + +func (_ *GetDot11StatusFunction) Request() interface{} { + return &GetDot11Status{} +} +func (_ *GetDot11StatusFunction) Response() interface{} { + return &GetDot11StatusResponse{} +} + +type GetDot1XConfigurationFunction struct{} + +func (_ *GetDot1XConfigurationFunction) Request() interface{} { + return &GetDot1XConfiguration{} +} +func (_ *GetDot1XConfigurationFunction) Response() interface{} { + return &GetDot1XConfigurationResponse{} +} + +type GetDot1XConfigurationsFunction struct{} + +func (_ *GetDot1XConfigurationsFunction) Request() interface{} { + return &GetDot1XConfigurations{} +} +func (_ *GetDot1XConfigurationsFunction) Response() interface{} { + return &GetDot1XConfigurationsResponse{} +} + +type GetDynamicDNSFunction struct{} + +func (_ *GetDynamicDNSFunction) Request() interface{} { + return &GetDynamicDNS{} +} +func (_ *GetDynamicDNSFunction) Response() interface{} { + return &GetDynamicDNSResponse{} +} + +type GetEndpointReferenceFunction struct{} + +func (_ *GetEndpointReferenceFunction) Request() interface{} { + return &GetEndpointReference{} +} +func (_ *GetEndpointReferenceFunction) Response() interface{} { + return &GetEndpointReferenceResponse{} +} + +type GetGeoLocationFunction struct{} + +func (_ *GetGeoLocationFunction) Request() interface{} { + return &GetGeoLocation{} +} +func (_ *GetGeoLocationFunction) Response() interface{} { + return &GetGeoLocationResponse{} +} + +type GetHostnameFunction struct{} + +func (_ *GetHostnameFunction) Request() interface{} { + return &GetHostname{} +} +func (_ *GetHostnameFunction) Response() interface{} { + return &GetHostnameResponse{} +} + +type GetIPAddressFilterFunction struct{} + +func (_ *GetIPAddressFilterFunction) Request() interface{} { + return &GetIPAddressFilter{} +} +func (_ *GetIPAddressFilterFunction) Response() interface{} { + return &GetIPAddressFilterResponse{} +} + +type GetNTPFunction struct{} + +func (_ *GetNTPFunction) Request() interface{} { + return &GetNTP{} +} +func (_ *GetNTPFunction) Response() interface{} { + return &GetNTPResponse{} +} + +type GetNetworkDefaultGatewayFunction struct{} + +func (_ *GetNetworkDefaultGatewayFunction) Request() interface{} { + return &GetNetworkDefaultGateway{} +} +func (_ *GetNetworkDefaultGatewayFunction) Response() interface{} { + return &GetNetworkDefaultGatewayResponse{} +} + +type GetNetworkInterfacesFunction struct{} + +func (_ *GetNetworkInterfacesFunction) Request() interface{} { + return &GetNetworkInterfaces{} +} +func (_ *GetNetworkInterfacesFunction) Response() interface{} { + return &GetNetworkInterfacesResponse{} +} + +type GetNetworkProtocolsFunction struct{} + +func (_ *GetNetworkProtocolsFunction) Request() interface{} { + return &GetNetworkProtocols{} +} +func (_ *GetNetworkProtocolsFunction) Response() interface{} { + return &GetNetworkProtocolsResponse{} +} + +type GetPkcs10RequestFunction struct{} + +func (_ *GetPkcs10RequestFunction) Request() interface{} { + return &GetPkcs10Request{} +} +func (_ *GetPkcs10RequestFunction) Response() interface{} { + return &GetPkcs10RequestResponse{} +} + +type GetRelayOutputsFunction struct{} + +func (_ *GetRelayOutputsFunction) Request() interface{} { + return &GetRelayOutputs{} +} +func (_ *GetRelayOutputsFunction) Response() interface{} { + return &GetRelayOutputsResponse{} +} + +type GetRemoteDiscoveryModeFunction struct{} + +func (_ *GetRemoteDiscoveryModeFunction) Request() interface{} { + return &GetRemoteDiscoveryMode{} +} +func (_ *GetRemoteDiscoveryModeFunction) Response() interface{} { + return &GetRemoteDiscoveryModeResponse{} +} + +type GetRemoteUserFunction struct{} + +func (_ *GetRemoteUserFunction) Request() interface{} { + return &GetRemoteUser{} +} +func (_ *GetRemoteUserFunction) Response() interface{} { + return &GetRemoteUserResponse{} +} + +type GetScopesFunction struct{} + +func (_ *GetScopesFunction) Request() interface{} { + return &GetScopes{} +} +func (_ *GetScopesFunction) Response() interface{} { + return &GetScopesResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type GetServicesFunction struct{} + +func (_ *GetServicesFunction) Request() interface{} { + return &GetServices{} +} +func (_ *GetServicesFunction) Response() interface{} { + return &GetServicesResponse{} +} + +type GetStorageConfigurationFunction struct{} + +func (_ *GetStorageConfigurationFunction) Request() interface{} { + return &GetStorageConfiguration{} +} +func (_ *GetStorageConfigurationFunction) Response() interface{} { + return &GetStorageConfigurationResponse{} +} + +type GetStorageConfigurationsFunction struct{} + +func (_ *GetStorageConfigurationsFunction) Request() interface{} { + return &GetStorageConfigurations{} +} +func (_ *GetStorageConfigurationsFunction) Response() interface{} { + return &GetStorageConfigurationsResponse{} +} + +type GetSystemBackupFunction struct{} + +func (_ *GetSystemBackupFunction) Request() interface{} { + return &GetSystemBackup{} +} +func (_ *GetSystemBackupFunction) Response() interface{} { + return &GetSystemBackupResponse{} +} + +type GetSystemDateAndTimeFunction struct{} + +func (_ *GetSystemDateAndTimeFunction) Request() interface{} { + return &GetSystemDateAndTime{} +} +func (_ *GetSystemDateAndTimeFunction) Response() interface{} { + return &GetSystemDateAndTimeResponse{} +} + +type GetSystemLogFunction struct{} + +func (_ *GetSystemLogFunction) Request() interface{} { + return &GetSystemLog{} +} +func (_ *GetSystemLogFunction) Response() interface{} { + return &GetSystemLogResponse{} +} + +type GetSystemSupportInformationFunction struct{} + +func (_ *GetSystemSupportInformationFunction) Request() interface{} { + return &GetSystemSupportInformation{} +} +func (_ *GetSystemSupportInformationFunction) Response() interface{} { + return &GetSystemSupportInformationResponse{} +} + +type GetSystemUrisFunction struct{} + +func (_ *GetSystemUrisFunction) Request() interface{} { + return &GetSystemUris{} +} +func (_ *GetSystemUrisFunction) Response() interface{} { + return &GetSystemUrisResponse{} +} + +type GetUsersFunction struct{} + +func (_ *GetUsersFunction) Request() interface{} { + return &GetUsers{} +} +func (_ *GetUsersFunction) Response() interface{} { + return &GetUsersResponse{} +} + +type GetWsdlUrlFunction struct{} + +func (_ *GetWsdlUrlFunction) Request() interface{} { + return &GetWsdlUrl{} +} +func (_ *GetWsdlUrlFunction) Response() interface{} { + return &GetWsdlUrlResponse{} +} + +type GetZeroConfigurationFunction struct{} + +func (_ *GetZeroConfigurationFunction) Request() interface{} { + return &GetZeroConfiguration{} +} +func (_ *GetZeroConfigurationFunction) Response() interface{} { + return &GetZeroConfigurationResponse{} +} + +type LoadCACertificatesFunction struct{} + +func (_ *LoadCACertificatesFunction) Request() interface{} { + return &LoadCACertificates{} +} +func (_ *LoadCACertificatesFunction) Response() interface{} { + return &LoadCACertificatesResponse{} +} + +type LoadCertificateWithPrivateKeyFunction struct{} + +func (_ *LoadCertificateWithPrivateKeyFunction) Request() interface{} { + return &LoadCertificateWithPrivateKey{} +} +func (_ *LoadCertificateWithPrivateKeyFunction) Response() interface{} { + return &LoadCertificateWithPrivateKeyResponse{} +} + +type LoadCertificatesFunction struct{} + +func (_ *LoadCertificatesFunction) Request() interface{} { + return &LoadCertificates{} +} +func (_ *LoadCertificatesFunction) Response() interface{} { + return &LoadCertificatesResponse{} +} + +type RemoveIPAddressFilterFunction struct{} + +func (_ *RemoveIPAddressFilterFunction) Request() interface{} { + return &RemoveIPAddressFilter{} +} +func (_ *RemoveIPAddressFilterFunction) Response() interface{} { + return &RemoveIPAddressFilterResponse{} +} + +type RemoveScopesFunction struct{} + +func (_ *RemoveScopesFunction) Request() interface{} { + return &RemoveScopes{} +} +func (_ *RemoveScopesFunction) Response() interface{} { + return &RemoveScopesResponse{} +} + +type RestoreSystemFunction struct{} + +func (_ *RestoreSystemFunction) Request() interface{} { + return &RestoreSystem{} +} +func (_ *RestoreSystemFunction) Response() interface{} { + return &RestoreSystemResponse{} +} + +type ScanAvailableDot11NetworksFunction struct{} + +func (_ *ScanAvailableDot11NetworksFunction) Request() interface{} { + return &ScanAvailableDot11Networks{} +} +func (_ *ScanAvailableDot11NetworksFunction) Response() interface{} { + return &ScanAvailableDot11NetworksResponse{} +} + +type SendAuxiliaryCommandFunction struct{} + +func (_ *SendAuxiliaryCommandFunction) Request() interface{} { + return &SendAuxiliaryCommand{} +} +func (_ *SendAuxiliaryCommandFunction) Response() interface{} { + return &SendAuxiliaryCommandResponse{} +} + +type SetAccessPolicyFunction struct{} + +func (_ *SetAccessPolicyFunction) Request() interface{} { + return &SetAccessPolicy{} +} +func (_ *SetAccessPolicyFunction) Response() interface{} { + return &SetAccessPolicyResponse{} +} + +type SetCertificatesStatusFunction struct{} + +func (_ *SetCertificatesStatusFunction) Request() interface{} { + return &SetCertificatesStatus{} +} +func (_ *SetCertificatesStatusFunction) Response() interface{} { + return &SetCertificatesStatusResponse{} +} + +type SetClientCertificateModeFunction struct{} + +func (_ *SetClientCertificateModeFunction) Request() interface{} { + return &SetClientCertificateMode{} +} +func (_ *SetClientCertificateModeFunction) Response() interface{} { + return &SetClientCertificateModeResponse{} +} + +type SetDNSFunction struct{} + +func (_ *SetDNSFunction) Request() interface{} { + return &SetDNS{} +} +func (_ *SetDNSFunction) Response() interface{} { + return &SetDNSResponse{} +} + +type SetDPAddressesFunction struct{} + +func (_ *SetDPAddressesFunction) Request() interface{} { + return &SetDPAddresses{} +} +func (_ *SetDPAddressesFunction) Response() interface{} { + return &SetDPAddressesResponse{} +} + +type SetDiscoveryModeFunction struct{} + +func (_ *SetDiscoveryModeFunction) Request() interface{} { + return &SetDiscoveryMode{} +} +func (_ *SetDiscoveryModeFunction) Response() interface{} { + return &SetDiscoveryModeResponse{} +} + +type SetDot1XConfigurationFunction struct{} + +func (_ *SetDot1XConfigurationFunction) Request() interface{} { + return &SetDot1XConfiguration{} +} +func (_ *SetDot1XConfigurationFunction) Response() interface{} { + return &SetDot1XConfigurationResponse{} +} + +type SetDynamicDNSFunction struct{} + +func (_ *SetDynamicDNSFunction) Request() interface{} { + return &SetDynamicDNS{} +} +func (_ *SetDynamicDNSFunction) Response() interface{} { + return &SetDynamicDNSResponse{} +} + +type SetGeoLocationFunction struct{} + +func (_ *SetGeoLocationFunction) Request() interface{} { + return &SetGeoLocation{} +} +func (_ *SetGeoLocationFunction) Response() interface{} { + return &SetGeoLocationResponse{} +} + +type SetHostnameFunction struct{} + +func (_ *SetHostnameFunction) Request() interface{} { + return &SetHostname{} +} +func (_ *SetHostnameFunction) Response() interface{} { + return &SetHostnameResponse{} +} + +type SetHostnameFromDHCPFunction struct{} + +func (_ *SetHostnameFromDHCPFunction) Request() interface{} { + return &SetHostnameFromDHCP{} +} +func (_ *SetHostnameFromDHCPFunction) Response() interface{} { + return &SetHostnameFromDHCPResponse{} +} + +type SetIPAddressFilterFunction struct{} + +func (_ *SetIPAddressFilterFunction) Request() interface{} { + return &SetIPAddressFilter{} +} +func (_ *SetIPAddressFilterFunction) Response() interface{} { + return &SetIPAddressFilterResponse{} +} + +type SetNTPFunction struct{} + +func (_ *SetNTPFunction) Request() interface{} { + return &SetNTP{} +} +func (_ *SetNTPFunction) Response() interface{} { + return &SetNTPResponse{} +} + +type SetNetworkDefaultGatewayFunction struct{} + +func (_ *SetNetworkDefaultGatewayFunction) Request() interface{} { + return &SetNetworkDefaultGateway{} +} +func (_ *SetNetworkDefaultGatewayFunction) Response() interface{} { + return &SetNetworkDefaultGatewayResponse{} +} + +type SetNetworkInterfacesFunction struct{} + +func (_ *SetNetworkInterfacesFunction) Request() interface{} { + return &SetNetworkInterfaces{} +} +func (_ *SetNetworkInterfacesFunction) Response() interface{} { + return &SetNetworkInterfacesResponse{} +} + +type SetNetworkProtocolsFunction struct{} + +func (_ *SetNetworkProtocolsFunction) Request() interface{} { + return &SetNetworkProtocols{} +} +func (_ *SetNetworkProtocolsFunction) Response() interface{} { + return &SetNetworkProtocolsResponse{} +} + +type SetRelayOutputSettingsFunction struct{} + +func (_ *SetRelayOutputSettingsFunction) Request() interface{} { + return &SetRelayOutputSettings{} +} +func (_ *SetRelayOutputSettingsFunction) Response() interface{} { + return &SetRelayOutputSettingsResponse{} +} + +type SetRelayOutputStateFunction struct{} + +func (_ *SetRelayOutputStateFunction) Request() interface{} { + return &SetRelayOutputState{} +} +func (_ *SetRelayOutputStateFunction) Response() interface{} { + return &SetRelayOutputStateResponse{} +} + +type SetRemoteDiscoveryModeFunction struct{} + +func (_ *SetRemoteDiscoveryModeFunction) Request() interface{} { + return &SetRemoteDiscoveryMode{} +} +func (_ *SetRemoteDiscoveryModeFunction) Response() interface{} { + return &SetRemoteDiscoveryModeResponse{} +} + +type SetRemoteUserFunction struct{} + +func (_ *SetRemoteUserFunction) Request() interface{} { + return &SetRemoteUser{} +} +func (_ *SetRemoteUserFunction) Response() interface{} { + return &SetRemoteUserResponse{} +} + +type SetScopesFunction struct{} + +func (_ *SetScopesFunction) Request() interface{} { + return &SetScopes{} +} +func (_ *SetScopesFunction) Response() interface{} { + return &SetScopesResponse{} +} + +type SetStorageConfigurationFunction struct{} + +func (_ *SetStorageConfigurationFunction) Request() interface{} { + return &SetStorageConfiguration{} +} +func (_ *SetStorageConfigurationFunction) Response() interface{} { + return &SetStorageConfigurationResponse{} +} + +type SetSystemDateAndTimeFunction struct{} + +func (_ *SetSystemDateAndTimeFunction) Request() interface{} { + return &SetSystemDateAndTime{} +} +func (_ *SetSystemDateAndTimeFunction) Response() interface{} { + return &SetSystemDateAndTimeResponse{} +} + +type SetSystemFactoryDefaultFunction struct{} + +func (_ *SetSystemFactoryDefaultFunction) Request() interface{} { + return &SetSystemFactoryDefault{} +} +func (_ *SetSystemFactoryDefaultFunction) Response() interface{} { + return &SetSystemFactoryDefaultResponse{} +} + +type SetUserFunction struct{} + +func (_ *SetUserFunction) Request() interface{} { + return &SetUser{} +} +func (_ *SetUserFunction) Response() interface{} { + return &SetUserResponse{} +} + +type SetZeroConfigurationFunction struct{} + +func (_ *SetZeroConfigurationFunction) Request() interface{} { + return &SetZeroConfiguration{} +} +func (_ *SetZeroConfigurationFunction) Response() interface{} { + return &SetZeroConfigurationResponse{} +} + +type StartFirmwareUpgradeFunction struct{} + +func (_ *StartFirmwareUpgradeFunction) Request() interface{} { + return &StartFirmwareUpgrade{} +} +func (_ *StartFirmwareUpgradeFunction) Response() interface{} { + return &StartFirmwareUpgradeResponse{} +} + +type StartSystemRestoreFunction struct{} + +func (_ *StartSystemRestoreFunction) Request() interface{} { + return &StartSystemRestore{} +} +func (_ *StartSystemRestoreFunction) Response() interface{} { + return &StartSystemRestoreResponse{} +} + +type SystemRebootFunction struct{} + +func (_ *SystemRebootFunction) Request() interface{} { + return &SystemReboot{} +} +func (_ *SystemRebootFunction) Response() interface{} { + return &SystemRebootResponse{} +} + +type UpgradeSystemFirmwareFunction struct{} + +func (_ *UpgradeSystemFirmwareFunction) Request() interface{} { + return &UpgradeSystemFirmware{} +} +func (_ *UpgradeSystemFirmwareFunction) Response() interface{} { + return &UpgradeSystemFirmwareResponse{} +} diff --git a/device/types.go b/device/types.go index 79ed755..210cd00 100644 --- a/device/types.go +++ b/device/types.go @@ -1,5 +1,7 @@ package device +//go:generate python3 ../python/gen_commands.py + import ( "github.com/kerberos-io/onvif/xsd" "github.com/kerberos-io/onvif/xsd/onvif" @@ -36,28 +38,24 @@ type NetworkCapabilities struct { } type SecurityCapabilities struct { - TLS1_0 xsd.Boolean `xml:"TLS1_0,attr"` - TLS1_1 xsd.Boolean `xml:"TLS1_1,attr"` - TLS1_2 xsd.Boolean `xml:"TLS1_2,attr"` - OnboardKeyGeneration xsd.Boolean `xml:"OnboardKeyGeneration,attr"` - AccessPolicyConfig xsd.Boolean `xml:"AccessPolicyConfig,attr"` - DefaultAccessPolicy xsd.Boolean `xml:"DefaultAccessPolicy,attr"` - Dot1X xsd.Boolean `xml:"Dot1X,attr"` - RemoteUserHandling xsd.Boolean `xml:"RemoteUserHandling,attr"` - X_509Token xsd.Boolean `xml:"X_509Token,attr"` - SAMLToken xsd.Boolean `xml:"SAMLToken,attr"` - KerberosToken xsd.Boolean `xml:"KerberosToken,attr"` - UsernameToken xsd.Boolean `xml:"UsernameToken,attr"` - HttpDigest xsd.Boolean `xml:"HttpDigest,attr"` - RELToken xsd.Boolean `xml:"RELToken,attr"` - SupportedEAPMethods EAPMethodTypes `xml:"SupportedEAPMethods,attr"` - MaxUsers int `xml:"MaxUsers,attr"` - MaxUserNameLength int `xml:"MaxUserNameLength,attr"` - MaxPasswordLength int `xml:"MaxPasswordLength,attr"` -} - -type EAPMethodTypes struct { - Types []int + TLS1_0 xsd.Boolean `xml:"TLS1_0,attr"` + TLS1_1 xsd.Boolean `xml:"TLS1_1,attr"` + TLS1_2 xsd.Boolean `xml:"TLS1_2,attr"` + OnboardKeyGeneration xsd.Boolean `xml:"OnboardKeyGeneration,attr"` + AccessPolicyConfig xsd.Boolean `xml:"AccessPolicyConfig,attr"` + DefaultAccessPolicy xsd.Boolean `xml:"DefaultAccessPolicy,attr"` + Dot1X xsd.Boolean `xml:"Dot1X,attr"` + RemoteUserHandling xsd.Boolean `xml:"RemoteUserHandling,attr"` + X_509Token xsd.Boolean `xml:"X_509Token,attr"` + SAMLToken xsd.Boolean `xml:"SAMLToken,attr"` + KerberosToken xsd.Boolean `xml:"KerberosToken,attr"` + UsernameToken xsd.Boolean `xml:"UsernameToken,attr"` + HttpDigest xsd.Boolean `xml:"HttpDigest,attr"` + RELToken xsd.Boolean `xml:"RELToken,attr"` + SupportedEAPMethods onvif.IntAttrList `xml:"SupportedEAPMethods,attr"` + MaxUsers int `xml:"MaxUsers,attr"` + MaxUserNameLength int `xml:"MaxUserNameLength,attr"` + MaxPasswordLength int `xml:"MaxPasswordLength,attr"` } type SystemCapabilities struct { @@ -73,6 +71,7 @@ type SystemCapabilities struct { HttpSupportInformation xsd.Boolean `xml:"HttpSupportInformation,attr"` StorageConfiguration xsd.Boolean `xml:"StorageConfiguration,attr"` MaxStorageConfigurations int `xml:"MaxStorageConfigurations,attr"` + StorageTypesSupported onvif.StringAttrList `xml:"StorageTypesSupported,attr"` GeoLocationEntries int `xml:"GeoLocationEntries,attr"` AutoGeo onvif.StringAttrList `xml:"AutoGeo,attr"` } @@ -83,11 +82,23 @@ type MiscCapabilities struct { type StorageConfiguration struct { onvif.DeviceEntity - Data StorageConfigurationData `xml:"tds:Data"` + Data struct { + Type xsd.String `xml:"type,attr"` + Region string + LocalPath xsd.AnyURI + StorageUri xsd.AnyURI + User struct { + UserName xsd.String + Password xsd.String `json:",omitempty"` + Extension xsd.AnyType `json:",omitempty"` + } + Extension xsd.AnyURI `json:",omitempty"` + } } type StorageConfigurationData struct { Type xsd.String `xml:"type,attr"` + Region string `xml:"tds:Region,omitempty"` LocalPath xsd.AnyURI `xml:"tds:LocalPath"` StorageUri xsd.AnyURI `xml:"tds:StorageUri"` User UserCredential `xml:"tds:User"` @@ -131,12 +142,14 @@ type GetDeviceInformationResponse struct { HardwareId string } +// SetSystemDateAndTime and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetSystemDateAndTime type SetSystemDateAndTime struct { - XMLName string `xml:"tds:SetSystemDateAndTime"` - DateTimeType onvif.SetDateTimeType `xml:"tds:DateTimeType"` - DaylightSavings xsd.Boolean `xml:"tds:DaylightSavings"` - TimeZone onvif.TimeZone `xml:"tds:TimeZone"` - UTCDateTime onvif.DateTime `xml:"tds:UTCDateTime"` + XMLName string `xml:"tds:SetSystemDateAndTime,omitempty"` + DateTimeType *onvif.SetDateTimeType `xml:"tds:DateTimeType,omitempty"` + DaylightSavings *xsd.Boolean `xml:"tds:DaylightSavings,omitempty"` + TimeZone *onvif.TimeZone `xml:"tds:TimeZone,omitempty"` + UTCDateTime *onvif.DateTimeRequest `xml:"tds:UTCDateTime,omitempty"` } type SetSystemDateAndTimeResponse struct { @@ -150,6 +163,8 @@ type GetSystemDateAndTimeResponse struct { SystemDateAndTime onvif.SystemDateTime } +// SetSystemFactoryDefault and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetSystemFactoryDefault type SetSystemFactoryDefault struct { XMLName string `xml:"tds:SetSystemFactoryDefault"` FactoryDefault onvif.FactoryDefaultType `xml:"tds:FactoryDefault"` @@ -167,6 +182,8 @@ type UpgradeSystemFirmwareResponse struct { Message string } +// SystemReboot and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SystemReboot type SystemReboot struct { XMLName string `xml:"tds:SystemReboot"` } @@ -175,7 +192,7 @@ type SystemRebootResponse struct { Message string } -//TODO: one or more repetitions +// TODO: one or more repetitions type RestoreSystem struct { XMLName string `xml:"tds:RestoreSystem"` BackupFiles onvif.BackupFile `xml:"tds:BackupFiles"` @@ -214,37 +231,40 @@ type GetScopes struct { } type GetScopesResponse struct { - Scopes onvif.Scope + Scopes []onvif.Scope } -//TODO: one or more scopes +// SetScopes and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetScopes type SetScopes struct { - XMLName string `xml:"tds:SetScopes"` - Scopes xsd.AnyURI `xml:"tds:Scopes"` + XMLName string `xml:"tds:SetScopes"` + Scopes []xsd.AnyURI `xml:"tds:Scopes"` } type SetScopesResponse struct { } -//TODO: list of scopes +// AddScopes and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.AddScopes type AddScopes struct { - XMLName string `xml:"tds:AddScopes"` - ScopeItem xsd.AnyURI `xml:"tds:ScopeItem"` + XMLName string `xml:"tds:AddScopes"` + ScopeItem []xsd.AnyURI `xml:"tds:ScopeItem"` } type AddScopesResponse struct { } -//TODO: One or more repetitions type RemoveScopes struct { - XMLName string `xml:"tds:RemoveScopes"` - ScopeItem xsd.AnyURI `xml:"onvif:ScopeItem"` + XMLName string `xml:"tds:RemoveScopes"` + ScopeItem []xsd.AnyURI `xml:"tds:ScopeItem"` } type RemoveScopesResponse struct { ScopeItem xsd.AnyURI } +// GetDiscoveryMode and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.GetDiscoveryMode type GetDiscoveryMode struct { XMLName string `xml:"tds:GetDiscoveryMode"` } @@ -253,6 +273,8 @@ type GetDiscoveryModeResponse struct { DiscoveryMode onvif.DiscoveryMode } +// SetDiscoveryMode and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetDiscoveryMode type SetDiscoveryMode struct { XMLName string `xml:"tds:SetDiscoveryMode"` DiscoveryMode onvif.DiscoveryMode `xml:"tds:DiscoveryMode"` @@ -322,30 +344,34 @@ type GetUsers struct { } type GetUsersResponse struct { - User onvif.User + User []onvif.User } -//TODO: List of users +// CreateUsers and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.CreateUsers type CreateUsers struct { - XMLName string `xml:"tds:CreateUsers"` - User onvif.User `xml:"tds:User,omitempty"` + XMLName string `xml:"tds:CreateUsers"` + User []onvif.UserRequest `xml:"tds:User,omitempty"` } type CreateUsersResponse struct { } -//TODO: one or more Username +// DeleteUsers and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.DeleteUsers type DeleteUsers struct { - XMLName xsd.String `xml:"tds:DeleteUsers"` - Username xsd.String `xml:"tds:Username"` + XMLName xsd.String `xml:"tds:DeleteUsers"` + Username []xsd.String `xml:"tds:Username"` } type DeleteUsersResponse struct { } +// SetUser and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetUser type SetUser struct { - XMLName string `xml:"tds:SetUser"` - User onvif.User `xml:"tds:User"` + XMLName string `xml:"tds:SetUser"` + User []onvif.UserRequest `xml:"tds:User"` } type SetUserResponse struct { @@ -360,8 +386,8 @@ type GetWsdlUrlResponse struct { } type GetCapabilities struct { - XMLName string `xml:"tds:GetCapabilities"` - Category onvif.CapabilityCategory `xml:"tds:Category"` + XMLName string `xml:"tds:GetCapabilities"` + Category []onvif.CapabilityCategory `xml:"tds:Category"` } type GetCapabilitiesResponse struct { @@ -402,10 +428,10 @@ type GetDNSResponse struct { } type SetDNS struct { - XMLName string `xml:"tds:SetDNS"` - FromDHCP xsd.Boolean `xml:"tds:FromDHCP"` - SearchDomain xsd.Token `xml:"tds:SearchDomain"` - DNSManual onvif.IPAddress `xml:"tds:DNSManual"` + XMLName string `xml:"tds:SetDNS"` + FromDHCP *xsd.Boolean `xml:"tds:FromDHCP,omitempty"` + SearchDomain *xsd.Token `xml:"tds:SearchDomain,omitempty"` + DNSManual *onvif.IPAddress `xml:"tds:DNSManual,omitempty"` } type SetDNSResponse struct { @@ -455,9 +481,9 @@ type GetNetworkInterfacesResponse struct { } type SetNetworkInterfaces struct { - XMLName string `xml:"tds:SetNetworkInterfaces"` - InterfaceToken onvif.ReferenceToken `xml:"tds:InterfaceToken"` - NetworkInterface onvif.NetworkInterfaceSetConfiguration `xml:"tds:NetworkInterface"` + XMLName string `xml:"tds:SetNetworkInterfaces"` + InterfaceToken *onvif.ReferenceToken `xml:"tds:InterfaceToken,omitempty"` + NetworkInterface *onvif.NetworkInterfaceSetConfiguration `xml:"tds:NetworkInterface,omitempty"` } type SetNetworkInterfacesResponse struct { @@ -469,12 +495,12 @@ type GetNetworkProtocols struct { } type GetNetworkProtocolsResponse struct { - NetworkProtocols onvif.NetworkProtocol + NetworkProtocols []onvif.NetworkProtocolResponse } type SetNetworkProtocols struct { - XMLName string `xml:"tds:SetNetworkProtocols"` - NetworkProtocols onvif.NetworkProtocol `xml:"tds:NetworkProtocols"` + XMLName string `xml:"tds:SetNetworkProtocols"` + NetworkProtocols []onvif.NetworkProtocolRequest `xml:"tds:NetworkProtocols"` } type SetNetworkProtocolsResponse struct { @@ -490,8 +516,8 @@ type GetNetworkDefaultGatewayResponse struct { type SetNetworkDefaultGateway struct { XMLName string `xml:"tds:SetNetworkDefaultGateway"` - IPv4Address onvif.IPv4Address `xml:"tds:IPv4Address"` - IPv6Address onvif.IPv6Address `xml:"tds:IPv6Address"` + IPv4Address onvif.IPv4Address `xml:"tds:IPv4Address,omitempty"` + IPv6Address onvif.IPv6Address `xml:"tds:IPv6Address,omitempty"` } type SetNetworkDefaultGatewayResponse struct { @@ -530,11 +556,11 @@ type SetIPAddressFilter struct { type SetIPAddressFilterResponse struct { } -//This operation adds an IP filter address to a device. -//If the device supports device access control based on -//IP filtering rules (denied or accepted ranges of IP addresses), -//the device shall support adding of IP filtering addresses through -//the AddIPAddressFilter command. +// This operation adds an IP filter address to a device. +// If the device supports device access control based on +// IP filtering rules (denied or accepted ranges of IP addresses), +// the device shall support adding of IP filtering addresses through +// the AddIPAddressFilter command. type AddIPAddressFilter struct { XMLName string `xml:"tds:AddIPAddressFilter"` IPAddressFilter onvif.IPAddressFilter `xml:"tds:IPAddressFilter"` @@ -545,7 +571,7 @@ type AddIPAddressFilterResponse struct { type RemoveIPAddressFilter struct { XMLName string `xml:"tds:RemoveIPAddressFilter"` - IPAddressFilter onvif.IPAddressFilter `xml:"onvif:IPAddressFilter"` + IPAddressFilter onvif.IPAddressFilter `xml:"IPAddressFilter"` } type RemoveIPAddressFilterResponse struct { @@ -603,7 +629,7 @@ type SetCertificatesStatus struct { type SetCertificatesStatusResponse struct { } -//TODO: List of CertificateID +// TODO: List of CertificateID type DeleteCertificates struct { XMLName string `xml:"tds:DeleteCertificates"` CertificateID xsd.Token `xml:"tds:CertificateID"` @@ -612,7 +638,7 @@ type DeleteCertificates struct { type DeleteCertificatesResponse struct { } -//TODO: Откуда onvif:data = cid:21312413412 +// TODO: Откуда onvif:data = cid:21312413412 type GetPkcs10Request struct { XMLName string `xml:"tds:GetPkcs10Request"` CertificateID xsd.Token `xml:"tds:CertificateID"` @@ -624,7 +650,7 @@ type GetPkcs10RequestResponse struct { Pkcs10Request onvif.BinaryData } -//TODO: one or more NTVCertificate +// TODO: one or more NTVCertificate type LoadCertificates struct { XMLName string `xml:"tds:LoadCertificates"` NVTCertificate onvif.Certificate `xml:"tds:NVTCertificate"` @@ -692,7 +718,7 @@ type GetCACertificatesResponse struct { CACertificate onvif.Certificate } -//TODO: one or more CertificateWithPrivateKey +// TODO: one or more CertificateWithPrivateKey type LoadCertificateWithPrivateKey struct { XMLName string `xml:"tds:LoadCertificateWithPrivateKey"` CertificateWithPrivateKey onvif.CertificateWithPrivateKey `xml:"tds:CertificateWithPrivateKey"` @@ -751,7 +777,7 @@ type GetDot1XConfigurationsResponse struct { Dot1XConfiguration onvif.Dot1XConfiguration } -//TODO: Zero or more Dot1XConfigurationToken +// TODO: Zero or more Dot1XConfigurationToken type DeleteDot1XConfiguration struct { XMLName string `xml:"tds:DeleteDot1XConfiguration"` Dot1XConfigurationToken onvif.ReferenceToken `xml:"tds:Dot1XConfigurationToken"` @@ -821,12 +847,12 @@ type GetStorageConfigurations struct { } type GetStorageConfigurationsResponse struct { - StorageConfigurations StorageConfiguration + StorageConfigurations []StorageConfiguration } type CreateStorageConfiguration struct { - XMLName string `xml:"tds:CreateStorageConfiguration"` - StorageConfiguration StorageConfigurationData + XMLName string `xml:"tds:CreateStorageConfiguration"` + StorageConfiguration StorageConfigurationData `xml:"tds:StorageConfiguration"` } type CreateStorageConfigurationResponse struct { @@ -843,8 +869,11 @@ type GetStorageConfigurationResponse struct { } type SetStorageConfiguration struct { - XMLName string `xml:"tds:SetStorageConfiguration"` - StorageConfiguration StorageConfiguration `xml:"tds:StorageConfiguration"` + XMLName string `xml:"tds:SetStorageConfiguration"` + StorageConfiguration struct { + Token xsd.String `xml:"token,attr"` + Data StorageConfigurationData `xml:"tds:Data"` + } `xml:"tds:StorageConfiguration"` } type SetStorageConfigurationResponse struct { @@ -866,7 +895,7 @@ type GetGeoLocationResponse struct { Location onvif.LocationEntity } -//TODO: one or more Location +// TODO: one or more Location type SetGeoLocation struct { XMLName string `xml:"tds:SetGeoLocation"` Location onvif.LocationEntity `xml:"tds:Location"` diff --git a/deviceio/function.go b/deviceio/function.go new file mode 100644 index 0000000..1e97bc6 --- /dev/null +++ b/deviceio/function.go @@ -0,0 +1,819 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package deviceio + +type AddIPAddressFilterFunction struct{} + +func (_ *AddIPAddressFilterFunction) Request() interface{} { + return &AddIPAddressFilter{} +} +func (_ *AddIPAddressFilterFunction) Response() interface{} { + return &AddIPAddressFilterResponse{} +} + +type AddScopesFunction struct{} + +func (_ *AddScopesFunction) Request() interface{} { + return &AddScopes{} +} +func (_ *AddScopesFunction) Response() interface{} { + return &AddScopesResponse{} +} + +type CreateCertificateFunction struct{} + +func (_ *CreateCertificateFunction) Request() interface{} { + return &CreateCertificate{} +} +func (_ *CreateCertificateFunction) Response() interface{} { + return &CreateCertificateResponse{} +} + +type CreateDot1XConfigurationFunction struct{} + +func (_ *CreateDot1XConfigurationFunction) Request() interface{} { + return &CreateDot1XConfiguration{} +} +func (_ *CreateDot1XConfigurationFunction) Response() interface{} { + return &CreateDot1XConfigurationResponse{} +} + +type CreateStorageConfigurationFunction struct{} + +func (_ *CreateStorageConfigurationFunction) Request() interface{} { + return &CreateStorageConfiguration{} +} +func (_ *CreateStorageConfigurationFunction) Response() interface{} { + return &CreateStorageConfigurationResponse{} +} + +type CreateUsersFunction struct{} + +func (_ *CreateUsersFunction) Request() interface{} { + return &CreateUsers{} +} +func (_ *CreateUsersFunction) Response() interface{} { + return &CreateUsersResponse{} +} + +type DeleteCertificatesFunction struct{} + +func (_ *DeleteCertificatesFunction) Request() interface{} { + return &DeleteCertificates{} +} +func (_ *DeleteCertificatesFunction) Response() interface{} { + return &DeleteCertificatesResponse{} +} + +type DeleteDot1XConfigurationFunction struct{} + +func (_ *DeleteDot1XConfigurationFunction) Request() interface{} { + return &DeleteDot1XConfiguration{} +} +func (_ *DeleteDot1XConfigurationFunction) Response() interface{} { + return &DeleteDot1XConfigurationResponse{} +} + +type DeleteGeoLocationFunction struct{} + +func (_ *DeleteGeoLocationFunction) Request() interface{} { + return &DeleteGeoLocation{} +} +func (_ *DeleteGeoLocationFunction) Response() interface{} { + return &DeleteGeoLocationResponse{} +} + +type DeleteStorageConfigurationFunction struct{} + +func (_ *DeleteStorageConfigurationFunction) Request() interface{} { + return &DeleteStorageConfiguration{} +} +func (_ *DeleteStorageConfigurationFunction) Response() interface{} { + return &DeleteStorageConfigurationResponse{} +} + +type DeleteUsersFunction struct{} + +func (_ *DeleteUsersFunction) Request() interface{} { + return &DeleteUsers{} +} +func (_ *DeleteUsersFunction) Response() interface{} { + return &DeleteUsersResponse{} +} + +type GetAccessPolicyFunction struct{} + +func (_ *GetAccessPolicyFunction) Request() interface{} { + return &GetAccessPolicy{} +} +func (_ *GetAccessPolicyFunction) Response() interface{} { + return &GetAccessPolicyResponse{} +} + +type GetCACertificatesFunction struct{} + +func (_ *GetCACertificatesFunction) Request() interface{} { + return &GetCACertificates{} +} +func (_ *GetCACertificatesFunction) Response() interface{} { + return &GetCACertificatesResponse{} +} + +type GetCapabilitiesFunction struct{} + +func (_ *GetCapabilitiesFunction) Request() interface{} { + return &GetCapabilities{} +} +func (_ *GetCapabilitiesFunction) Response() interface{} { + return &GetCapabilitiesResponse{} +} + +type GetCertificateInformationFunction struct{} + +func (_ *GetCertificateInformationFunction) Request() interface{} { + return &GetCertificateInformation{} +} +func (_ *GetCertificateInformationFunction) Response() interface{} { + return &GetCertificateInformationResponse{} +} + +type GetCertificatesFunction struct{} + +func (_ *GetCertificatesFunction) Request() interface{} { + return &GetCertificates{} +} +func (_ *GetCertificatesFunction) Response() interface{} { + return &GetCertificatesResponse{} +} + +type GetCertificatesStatusFunction struct{} + +func (_ *GetCertificatesStatusFunction) Request() interface{} { + return &GetCertificatesStatus{} +} +func (_ *GetCertificatesStatusFunction) Response() interface{} { + return &GetCertificatesStatusResponse{} +} + +type GetClientCertificateModeFunction struct{} + +func (_ *GetClientCertificateModeFunction) Request() interface{} { + return &GetClientCertificateMode{} +} +func (_ *GetClientCertificateModeFunction) Response() interface{} { + return &GetClientCertificateModeResponse{} +} + +type GetDNSFunction struct{} + +func (_ *GetDNSFunction) Request() interface{} { + return &GetDNS{} +} +func (_ *GetDNSFunction) Response() interface{} { + return &GetDNSResponse{} +} + +type GetDPAddressesFunction struct{} + +func (_ *GetDPAddressesFunction) Request() interface{} { + return &GetDPAddresses{} +} +func (_ *GetDPAddressesFunction) Response() interface{} { + return &GetDPAddressesResponse{} +} + +type GetDeviceInformationFunction struct{} + +func (_ *GetDeviceInformationFunction) Request() interface{} { + return &GetDeviceInformation{} +} +func (_ *GetDeviceInformationFunction) Response() interface{} { + return &GetDeviceInformationResponse{} +} + +type GetDiscoveryModeFunction struct{} + +func (_ *GetDiscoveryModeFunction) Request() interface{} { + return &GetDiscoveryMode{} +} +func (_ *GetDiscoveryModeFunction) Response() interface{} { + return &GetDiscoveryModeResponse{} +} + +type GetDot11CapabilitiesFunction struct{} + +func (_ *GetDot11CapabilitiesFunction) Request() interface{} { + return &GetDot11Capabilities{} +} +func (_ *GetDot11CapabilitiesFunction) Response() interface{} { + return &GetDot11CapabilitiesResponse{} +} + +type GetDot11StatusFunction struct{} + +func (_ *GetDot11StatusFunction) Request() interface{} { + return &GetDot11Status{} +} +func (_ *GetDot11StatusFunction) Response() interface{} { + return &GetDot11StatusResponse{} +} + +type GetDot1XConfigurationFunction struct{} + +func (_ *GetDot1XConfigurationFunction) Request() interface{} { + return &GetDot1XConfiguration{} +} +func (_ *GetDot1XConfigurationFunction) Response() interface{} { + return &GetDot1XConfigurationResponse{} +} + +type GetDot1XConfigurationsFunction struct{} + +func (_ *GetDot1XConfigurationsFunction) Request() interface{} { + return &GetDot1XConfigurations{} +} +func (_ *GetDot1XConfigurationsFunction) Response() interface{} { + return &GetDot1XConfigurationsResponse{} +} + +type GetDynamicDNSFunction struct{} + +func (_ *GetDynamicDNSFunction) Request() interface{} { + return &GetDynamicDNS{} +} +func (_ *GetDynamicDNSFunction) Response() interface{} { + return &GetDynamicDNSResponse{} +} + +type GetEndpointReferenceFunction struct{} + +func (_ *GetEndpointReferenceFunction) Request() interface{} { + return &GetEndpointReference{} +} +func (_ *GetEndpointReferenceFunction) Response() interface{} { + return &GetEndpointReferenceResponse{} +} + +type GetGeoLocationFunction struct{} + +func (_ *GetGeoLocationFunction) Request() interface{} { + return &GetGeoLocation{} +} +func (_ *GetGeoLocationFunction) Response() interface{} { + return &GetGeoLocationResponse{} +} + +type GetHostnameFunction struct{} + +func (_ *GetHostnameFunction) Request() interface{} { + return &GetHostname{} +} +func (_ *GetHostnameFunction) Response() interface{} { + return &GetHostnameResponse{} +} + +type GetIPAddressFilterFunction struct{} + +func (_ *GetIPAddressFilterFunction) Request() interface{} { + return &GetIPAddressFilter{} +} +func (_ *GetIPAddressFilterFunction) Response() interface{} { + return &GetIPAddressFilterResponse{} +} + +type GetNTPFunction struct{} + +func (_ *GetNTPFunction) Request() interface{} { + return &GetNTP{} +} +func (_ *GetNTPFunction) Response() interface{} { + return &GetNTPResponse{} +} + +type GetNetworkDefaultGatewayFunction struct{} + +func (_ *GetNetworkDefaultGatewayFunction) Request() interface{} { + return &GetNetworkDefaultGateway{} +} +func (_ *GetNetworkDefaultGatewayFunction) Response() interface{} { + return &GetNetworkDefaultGatewayResponse{} +} + +type GetNetworkInterfacesFunction struct{} + +func (_ *GetNetworkInterfacesFunction) Request() interface{} { + return &GetNetworkInterfaces{} +} +func (_ *GetNetworkInterfacesFunction) Response() interface{} { + return &GetNetworkInterfacesResponse{} +} + +type GetNetworkProtocolsFunction struct{} + +func (_ *GetNetworkProtocolsFunction) Request() interface{} { + return &GetNetworkProtocols{} +} +func (_ *GetNetworkProtocolsFunction) Response() interface{} { + return &GetNetworkProtocolsResponse{} +} + +type GetPkcs10RequestFunction struct{} + +func (_ *GetPkcs10RequestFunction) Request() interface{} { + return &GetPkcs10Request{} +} +func (_ *GetPkcs10RequestFunction) Response() interface{} { + return &GetPkcs10RequestResponse{} +} + +type GetRelayOutputsFunction struct{} + +func (_ *GetRelayOutputsFunction) Request() interface{} { + return &GetRelayOutputs{} +} +func (_ *GetRelayOutputsFunction) Response() interface{} { + return &GetRelayOutputsResponse{} +} + +type GetRemoteDiscoveryModeFunction struct{} + +func (_ *GetRemoteDiscoveryModeFunction) Request() interface{} { + return &GetRemoteDiscoveryMode{} +} +func (_ *GetRemoteDiscoveryModeFunction) Response() interface{} { + return &GetRemoteDiscoveryModeResponse{} +} + +type GetRemoteUserFunction struct{} + +func (_ *GetRemoteUserFunction) Request() interface{} { + return &GetRemoteUser{} +} +func (_ *GetRemoteUserFunction) Response() interface{} { + return &GetRemoteUserResponse{} +} + +type GetScopesFunction struct{} + +func (_ *GetScopesFunction) Request() interface{} { + return &GetScopes{} +} +func (_ *GetScopesFunction) Response() interface{} { + return &GetScopesResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type GetServicesFunction struct{} + +func (_ *GetServicesFunction) Request() interface{} { + return &GetServices{} +} +func (_ *GetServicesFunction) Response() interface{} { + return &GetServicesResponse{} +} + +type GetStorageConfigurationFunction struct{} + +func (_ *GetStorageConfigurationFunction) Request() interface{} { + return &GetStorageConfiguration{} +} +func (_ *GetStorageConfigurationFunction) Response() interface{} { + return &GetStorageConfigurationResponse{} +} + +type GetStorageConfigurationsFunction struct{} + +func (_ *GetStorageConfigurationsFunction) Request() interface{} { + return &GetStorageConfigurations{} +} +func (_ *GetStorageConfigurationsFunction) Response() interface{} { + return &GetStorageConfigurationsResponse{} +} + +type GetSystemBackupFunction struct{} + +func (_ *GetSystemBackupFunction) Request() interface{} { + return &GetSystemBackup{} +} +func (_ *GetSystemBackupFunction) Response() interface{} { + return &GetSystemBackupResponse{} +} + +type GetSystemDateAndTimeFunction struct{} + +func (_ *GetSystemDateAndTimeFunction) Request() interface{} { + return &GetSystemDateAndTime{} +} +func (_ *GetSystemDateAndTimeFunction) Response() interface{} { + return &GetSystemDateAndTimeResponse{} +} + +type GetSystemLogFunction struct{} + +func (_ *GetSystemLogFunction) Request() interface{} { + return &GetSystemLog{} +} +func (_ *GetSystemLogFunction) Response() interface{} { + return &GetSystemLogResponse{} +} + +type GetSystemSupportInformationFunction struct{} + +func (_ *GetSystemSupportInformationFunction) Request() interface{} { + return &GetSystemSupportInformation{} +} +func (_ *GetSystemSupportInformationFunction) Response() interface{} { + return &GetSystemSupportInformationResponse{} +} + +type GetSystemUrisFunction struct{} + +func (_ *GetSystemUrisFunction) Request() interface{} { + return &GetSystemUris{} +} +func (_ *GetSystemUrisFunction) Response() interface{} { + return &GetSystemUrisResponse{} +} + +type GetUsersFunction struct{} + +func (_ *GetUsersFunction) Request() interface{} { + return &GetUsers{} +} +func (_ *GetUsersFunction) Response() interface{} { + return &GetUsersResponse{} +} + +type GetWsdlUrlFunction struct{} + +func (_ *GetWsdlUrlFunction) Request() interface{} { + return &GetWsdlUrl{} +} +func (_ *GetWsdlUrlFunction) Response() interface{} { + return &GetWsdlUrlResponse{} +} + +type GetZeroConfigurationFunction struct{} + +func (_ *GetZeroConfigurationFunction) Request() interface{} { + return &GetZeroConfiguration{} +} +func (_ *GetZeroConfigurationFunction) Response() interface{} { + return &GetZeroConfigurationResponse{} +} + +type LoadCACertificatesFunction struct{} + +func (_ *LoadCACertificatesFunction) Request() interface{} { + return &LoadCACertificates{} +} +func (_ *LoadCACertificatesFunction) Response() interface{} { + return &LoadCACertificatesResponse{} +} + +type LoadCertificateWithPrivateKeyFunction struct{} + +func (_ *LoadCertificateWithPrivateKeyFunction) Request() interface{} { + return &LoadCertificateWithPrivateKey{} +} +func (_ *LoadCertificateWithPrivateKeyFunction) Response() interface{} { + return &LoadCertificateWithPrivateKeyResponse{} +} + +type LoadCertificatesFunction struct{} + +func (_ *LoadCertificatesFunction) Request() interface{} { + return &LoadCertificates{} +} +func (_ *LoadCertificatesFunction) Response() interface{} { + return &LoadCertificatesResponse{} +} + +type RemoveIPAddressFilterFunction struct{} + +func (_ *RemoveIPAddressFilterFunction) Request() interface{} { + return &RemoveIPAddressFilter{} +} +func (_ *RemoveIPAddressFilterFunction) Response() interface{} { + return &RemoveIPAddressFilterResponse{} +} + +type RemoveScopesFunction struct{} + +func (_ *RemoveScopesFunction) Request() interface{} { + return &RemoveScopes{} +} +func (_ *RemoveScopesFunction) Response() interface{} { + return &RemoveScopesResponse{} +} + +type RestoreSystemFunction struct{} + +func (_ *RestoreSystemFunction) Request() interface{} { + return &RestoreSystem{} +} +func (_ *RestoreSystemFunction) Response() interface{} { + return &RestoreSystemResponse{} +} + +type ScanAvailableDot11NetworksFunction struct{} + +func (_ *ScanAvailableDot11NetworksFunction) Request() interface{} { + return &ScanAvailableDot11Networks{} +} +func (_ *ScanAvailableDot11NetworksFunction) Response() interface{} { + return &ScanAvailableDot11NetworksResponse{} +} + +type SendAuxiliaryCommandFunction struct{} + +func (_ *SendAuxiliaryCommandFunction) Request() interface{} { + return &SendAuxiliaryCommand{} +} +func (_ *SendAuxiliaryCommandFunction) Response() interface{} { + return &SendAuxiliaryCommandResponse{} +} + +type SetAccessPolicyFunction struct{} + +func (_ *SetAccessPolicyFunction) Request() interface{} { + return &SetAccessPolicy{} +} +func (_ *SetAccessPolicyFunction) Response() interface{} { + return &SetAccessPolicyResponse{} +} + +type SetCertificatesStatusFunction struct{} + +func (_ *SetCertificatesStatusFunction) Request() interface{} { + return &SetCertificatesStatus{} +} +func (_ *SetCertificatesStatusFunction) Response() interface{} { + return &SetCertificatesStatusResponse{} +} + +type SetClientCertificateModeFunction struct{} + +func (_ *SetClientCertificateModeFunction) Request() interface{} { + return &SetClientCertificateMode{} +} +func (_ *SetClientCertificateModeFunction) Response() interface{} { + return &SetClientCertificateModeResponse{} +} + +type SetDNSFunction struct{} + +func (_ *SetDNSFunction) Request() interface{} { + return &SetDNS{} +} +func (_ *SetDNSFunction) Response() interface{} { + return &SetDNSResponse{} +} + +type SetDPAddressesFunction struct{} + +func (_ *SetDPAddressesFunction) Request() interface{} { + return &SetDPAddresses{} +} +func (_ *SetDPAddressesFunction) Response() interface{} { + return &SetDPAddressesResponse{} +} + +type SetDiscoveryModeFunction struct{} + +func (_ *SetDiscoveryModeFunction) Request() interface{} { + return &SetDiscoveryMode{} +} +func (_ *SetDiscoveryModeFunction) Response() interface{} { + return &SetDiscoveryModeResponse{} +} + +type SetDot1XConfigurationFunction struct{} + +func (_ *SetDot1XConfigurationFunction) Request() interface{} { + return &SetDot1XConfiguration{} +} +func (_ *SetDot1XConfigurationFunction) Response() interface{} { + return &SetDot1XConfigurationResponse{} +} + +type SetDynamicDNSFunction struct{} + +func (_ *SetDynamicDNSFunction) Request() interface{} { + return &SetDynamicDNS{} +} +func (_ *SetDynamicDNSFunction) Response() interface{} { + return &SetDynamicDNSResponse{} +} + +type SetGeoLocationFunction struct{} + +func (_ *SetGeoLocationFunction) Request() interface{} { + return &SetGeoLocation{} +} +func (_ *SetGeoLocationFunction) Response() interface{} { + return &SetGeoLocationResponse{} +} + +type SetHostnameFunction struct{} + +func (_ *SetHostnameFunction) Request() interface{} { + return &SetHostname{} +} +func (_ *SetHostnameFunction) Response() interface{} { + return &SetHostnameResponse{} +} + +type SetHostnameFromDHCPFunction struct{} + +func (_ *SetHostnameFromDHCPFunction) Request() interface{} { + return &SetHostnameFromDHCP{} +} +func (_ *SetHostnameFromDHCPFunction) Response() interface{} { + return &SetHostnameFromDHCPResponse{} +} + +type SetIPAddressFilterFunction struct{} + +func (_ *SetIPAddressFilterFunction) Request() interface{} { + return &SetIPAddressFilter{} +} +func (_ *SetIPAddressFilterFunction) Response() interface{} { + return &SetIPAddressFilterResponse{} +} + +type SetNTPFunction struct{} + +func (_ *SetNTPFunction) Request() interface{} { + return &SetNTP{} +} +func (_ *SetNTPFunction) Response() interface{} { + return &SetNTPResponse{} +} + +type SetNetworkDefaultGatewayFunction struct{} + +func (_ *SetNetworkDefaultGatewayFunction) Request() interface{} { + return &SetNetworkDefaultGateway{} +} +func (_ *SetNetworkDefaultGatewayFunction) Response() interface{} { + return &SetNetworkDefaultGatewayResponse{} +} + +type SetNetworkInterfacesFunction struct{} + +func (_ *SetNetworkInterfacesFunction) Request() interface{} { + return &SetNetworkInterfaces{} +} +func (_ *SetNetworkInterfacesFunction) Response() interface{} { + return &SetNetworkInterfacesResponse{} +} + +type SetNetworkProtocolsFunction struct{} + +func (_ *SetNetworkProtocolsFunction) Request() interface{} { + return &SetNetworkProtocols{} +} +func (_ *SetNetworkProtocolsFunction) Response() interface{} { + return &SetNetworkProtocolsResponse{} +} + +type SetRelayOutputSettingsFunction struct{} + +func (_ *SetRelayOutputSettingsFunction) Request() interface{} { + return &SetRelayOutputSettings{} +} +func (_ *SetRelayOutputSettingsFunction) Response() interface{} { + return &SetRelayOutputSettingsResponse{} +} + +type SetRelayOutputStateFunction struct{} + +func (_ *SetRelayOutputStateFunction) Request() interface{} { + return &SetRelayOutputState{} +} +func (_ *SetRelayOutputStateFunction) Response() interface{} { + return &SetRelayOutputStateResponse{} +} + +type SetRemoteDiscoveryModeFunction struct{} + +func (_ *SetRemoteDiscoveryModeFunction) Request() interface{} { + return &SetRemoteDiscoveryMode{} +} +func (_ *SetRemoteDiscoveryModeFunction) Response() interface{} { + return &SetRemoteDiscoveryModeResponse{} +} + +type SetRemoteUserFunction struct{} + +func (_ *SetRemoteUserFunction) Request() interface{} { + return &SetRemoteUser{} +} +func (_ *SetRemoteUserFunction) Response() interface{} { + return &SetRemoteUserResponse{} +} + +type SetScopesFunction struct{} + +func (_ *SetScopesFunction) Request() interface{} { + return &SetScopes{} +} +func (_ *SetScopesFunction) Response() interface{} { + return &SetScopesResponse{} +} + +type SetStorageConfigurationFunction struct{} + +func (_ *SetStorageConfigurationFunction) Request() interface{} { + return &SetStorageConfiguration{} +} +func (_ *SetStorageConfigurationFunction) Response() interface{} { + return &SetStorageConfigurationResponse{} +} + +type SetSystemDateAndTimeFunction struct{} + +func (_ *SetSystemDateAndTimeFunction) Request() interface{} { + return &SetSystemDateAndTime{} +} +func (_ *SetSystemDateAndTimeFunction) Response() interface{} { + return &SetSystemDateAndTimeResponse{} +} + +type SetSystemFactoryDefaultFunction struct{} + +func (_ *SetSystemFactoryDefaultFunction) Request() interface{} { + return &SetSystemFactoryDefault{} +} +func (_ *SetSystemFactoryDefaultFunction) Response() interface{} { + return &SetSystemFactoryDefaultResponse{} +} + +type SetUserFunction struct{} + +func (_ *SetUserFunction) Request() interface{} { + return &SetUser{} +} +func (_ *SetUserFunction) Response() interface{} { + return &SetUserResponse{} +} + +type SetZeroConfigurationFunction struct{} + +func (_ *SetZeroConfigurationFunction) Request() interface{} { + return &SetZeroConfiguration{} +} +func (_ *SetZeroConfigurationFunction) Response() interface{} { + return &SetZeroConfigurationResponse{} +} + +type StartFirmwareUpgradeFunction struct{} + +func (_ *StartFirmwareUpgradeFunction) Request() interface{} { + return &StartFirmwareUpgrade{} +} +func (_ *StartFirmwareUpgradeFunction) Response() interface{} { + return &StartFirmwareUpgradeResponse{} +} + +type StartSystemRestoreFunction struct{} + +func (_ *StartSystemRestoreFunction) Request() interface{} { + return &StartSystemRestore{} +} +func (_ *StartSystemRestoreFunction) Response() interface{} { + return &StartSystemRestoreResponse{} +} + +type SystemRebootFunction struct{} + +func (_ *SystemRebootFunction) Request() interface{} { + return &SystemReboot{} +} +func (_ *SystemRebootFunction) Response() interface{} { + return &SystemRebootResponse{} +} + +type UpgradeSystemFirmwareFunction struct{} + +func (_ *UpgradeSystemFirmwareFunction) Request() interface{} { + return &UpgradeSystemFirmware{} +} +func (_ *UpgradeSystemFirmwareFunction) Response() interface{} { + return &UpgradeSystemFirmwareResponse{} +} diff --git a/deviceio/types.go b/deviceio/types.go new file mode 100644 index 0000000..f6a3b3a --- /dev/null +++ b/deviceio/types.go @@ -0,0 +1,913 @@ +package deviceio + +//go:generate python3 ../python/gen_commands.py + +import ( + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" +) + +type Service struct { + Namespace xsd.AnyURI + XAddr xsd.AnyURI + Capabilities + Version onvif.OnvifVersion +} + +type Capabilities struct { + Any string +} + +type DeviceServiceCapabilities struct { + Network NetworkCapabilities + Security SecurityCapabilities + System SystemCapabilities + Misc MiscCapabilities +} + +type NetworkCapabilities struct { + IPFilter xsd.Boolean `xml:"IPFilter,attr"` + ZeroConfiguration xsd.Boolean `xml:"ZeroConfiguration,attr"` + IPVersion6 xsd.Boolean `xml:"IPVersion6,attr"` + DynDNS xsd.Boolean `xml:"DynDNS,attr"` + Dot11Configuration xsd.Boolean `xml:"Dot11Configuration,attr"` + Dot1XConfigurations int `xml:"Dot1XConfigurations,attr"` + HostnameFromDHCP xsd.Boolean `xml:"HostnameFromDHCP,attr"` + NTP int `xml:"NTP,attr"` + DHCPv6 xsd.Boolean `xml:"DHCPv6,attr"` +} + +type SecurityCapabilities struct { + TLS1_0 xsd.Boolean `xml:"TLS1_0,attr"` + TLS1_1 xsd.Boolean `xml:"TLS1_1,attr"` + TLS1_2 xsd.Boolean `xml:"TLS1_2,attr"` + OnboardKeyGeneration xsd.Boolean `xml:"OnboardKeyGeneration,attr"` + AccessPolicyConfig xsd.Boolean `xml:"AccessPolicyConfig,attr"` + DefaultAccessPolicy xsd.Boolean `xml:"DefaultAccessPolicy,attr"` + Dot1X xsd.Boolean `xml:"Dot1X,attr"` + RemoteUserHandling xsd.Boolean `xml:"RemoteUserHandling,attr"` + X_509Token xsd.Boolean `xml:"X_509Token,attr"` + SAMLToken xsd.Boolean `xml:"SAMLToken,attr"` + KerberosToken xsd.Boolean `xml:"KerberosToken,attr"` + UsernameToken xsd.Boolean `xml:"UsernameToken,attr"` + HttpDigest xsd.Boolean `xml:"HttpDigest,attr"` + RELToken xsd.Boolean `xml:"RELToken,attr"` + SupportedEAPMethods onvif.IntAttrList `xml:"SupportedEAPMethods,attr"` + MaxUsers int `xml:"MaxUsers,attr"` + MaxUserNameLength int `xml:"MaxUserNameLength,attr"` + MaxPasswordLength int `xml:"MaxPasswordLength,attr"` +} + +type SystemCapabilities struct { + DiscoveryResolve xsd.Boolean `xml:"DiscoveryResolve,attr"` + DiscoveryBye xsd.Boolean `xml:"DiscoveryBye,attr"` + RemoteDiscovery xsd.Boolean `xml:"RemoteDiscovery,attr"` + SystemBackup xsd.Boolean `xml:"SystemBackup,attr"` + SystemLogging xsd.Boolean `xml:"SystemLogging,attr"` + FirmwareUpgrade xsd.Boolean `xml:"FirmwareUpgrade,attr"` + HttpFirmwareUpgrade xsd.Boolean `xml:"HttpFirmwareUpgrade,attr"` + HttpSystemBackup xsd.Boolean `xml:"HttpSystemBackup,attr"` + HttpSystemLogging xsd.Boolean `xml:"HttpSystemLogging,attr"` + HttpSupportInformation xsd.Boolean `xml:"HttpSupportInformation,attr"` + StorageConfiguration xsd.Boolean `xml:"StorageConfiguration,attr"` + MaxStorageConfigurations int `xml:"MaxStorageConfigurations,attr"` + StorageTypesSupported onvif.StringAttrList `xml:"StorageTypesSupported,attr"` + GeoLocationEntries int `xml:"GeoLocationEntries,attr"` + AutoGeo onvif.StringAttrList `xml:"AutoGeo,attr"` +} + +type MiscCapabilities struct { + AuxiliaryCommands onvif.StringAttrList `xml:"AuxiliaryCommands,attr"` +} + +type StorageConfiguration struct { + onvif.DeviceEntity + Data struct { + Type xsd.String `xml:"type,attr"` + Region string + LocalPath xsd.AnyURI + StorageUri xsd.AnyURI + User struct { + UserName xsd.String + Password xsd.String `json:",omitempty"` + Extension xsd.AnyType `json:",omitempty"` + } + Extension xsd.AnyURI `json:",omitempty"` + } +} + +type StorageConfigurationData struct { + Type xsd.String `xml:"type,attr"` + Region string `xml:"tds:Region,omitempty"` + LocalPath xsd.AnyURI `xml:"tds:LocalPath"` + StorageUri xsd.AnyURI `xml:"tds:StorageUri"` + User UserCredential `xml:"tds:User"` + Extension xsd.AnyURI `xml:"tds:Extension"` +} + +type UserCredential struct { + UserName xsd.String `xml:"tds:UserName"` + Password xsd.String `xml:"tds:Password"` + Extension xsd.AnyType `xml:"tds:Extension"` +} + +//Device main types + +type GetServices struct { + XMLName string `xml:"tds:GetServices"` + IncludeCapability xsd.Boolean `xml:"tds:IncludeCapability"` +} + +type GetServicesResponse struct { + Service Service +} + +type GetServiceCapabilities struct { + XMLName string `xml:"tds:GetServiceCapabilities"` +} + +type GetServiceCapabilitiesResponse struct { + Capabilities DeviceServiceCapabilities +} + +type GetDeviceInformation struct { + XMLName string `xml:"tds:GetDeviceInformation"` +} + +type GetDeviceInformationResponse struct { + Manufacturer string + Model string + FirmwareVersion string + SerialNumber string + HardwareId string +} + +// SetSystemDateAndTime and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetSystemDateAndTime +type SetSystemDateAndTime struct { + XMLName string `xml:"tds:SetSystemDateAndTime,omitempty"` + DateTimeType *onvif.SetDateTimeType `xml:"tds:DateTimeType,omitempty"` + DaylightSavings *xsd.Boolean `xml:"tds:DaylightSavings,omitempty"` + TimeZone *onvif.TimeZone `xml:"tds:TimeZone,omitempty"` + UTCDateTime *onvif.DateTimeRequest `xml:"tds:UTCDateTime,omitempty"` +} + +type SetSystemDateAndTimeResponse struct { +} + +type GetSystemDateAndTime struct { + XMLName string `xml:"tds:GetSystemDateAndTime"` +} + +type GetSystemDateAndTimeResponse struct { + SystemDateAndTime onvif.SystemDateTime +} + +// SetSystemFactoryDefault and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetSystemFactoryDefault +type SetSystemFactoryDefault struct { + XMLName string `xml:"tds:SetSystemFactoryDefault"` + FactoryDefault onvif.FactoryDefaultType `xml:"tds:FactoryDefault"` +} + +type SetSystemFactoryDefaultResponse struct { +} + +type UpgradeSystemFirmware struct { + XMLName string `xml:"tds:UpgradeSystemFirmware"` + Firmware onvif.AttachmentData `xml:"tds:Firmware"` +} + +type UpgradeSystemFirmwareResponse struct { + Message string +} + +// SystemReboot and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SystemReboot +type SystemReboot struct { + XMLName string `xml:"tds:SystemReboot"` +} + +type SystemRebootResponse struct { + Message string +} + +// TODO: one or more repetitions +type RestoreSystem struct { + XMLName string `xml:"tds:RestoreSystem"` + BackupFiles onvif.BackupFile `xml:"tds:BackupFiles"` +} + +type RestoreSystemResponse struct { +} + +type GetSystemBackup struct { + XMLName string `xml:"tds:GetSystemBackup"` +} + +type GetSystemBackupResponse struct { + BackupFiles onvif.BackupFile +} + +type GetSystemLog struct { + XMLName string `xml:"tds:GetSystemLog"` + LogType onvif.SystemLogType `xml:"tds:LogType"` +} + +type GetSystemLogResponse struct { + SystemLog onvif.SystemLog +} + +type GetSystemSupportInformation struct { + XMLName string `xml:"tds:GetSystemSupportInformation"` +} + +type GetSystemSupportInformationResponse struct { + SupportInformation onvif.SupportInformation +} + +type GetScopes struct { + XMLName string `xml:"tds:GetScopes"` +} + +type GetScopesResponse struct { + Scopes []onvif.Scope +} + +// SetScopes and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetScopes +type SetScopes struct { + XMLName string `xml:"tds:SetScopes"` + Scopes []xsd.AnyURI `xml:"tds:Scopes"` +} + +type SetScopesResponse struct { +} + +// AddScopes and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.AddScopes +type AddScopes struct { + XMLName string `xml:"tds:AddScopes"` + ScopeItem []xsd.AnyURI `xml:"tds:ScopeItem"` +} + +type AddScopesResponse struct { +} + +type RemoveScopes struct { + XMLName string `xml:"tds:RemoveScopes"` + ScopeItem []xsd.AnyURI `xml:"tds:ScopeItem"` +} + +type RemoveScopesResponse struct { + ScopeItem xsd.AnyURI +} + +// GetDiscoveryMode and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.GetDiscoveryMode +type GetDiscoveryMode struct { + XMLName string `xml:"tds:GetDiscoveryMode"` +} + +type GetDiscoveryModeResponse struct { + DiscoveryMode onvif.DiscoveryMode +} + +// SetDiscoveryMode and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetDiscoveryMode +type SetDiscoveryMode struct { + XMLName string `xml:"tds:SetDiscoveryMode"` + DiscoveryMode onvif.DiscoveryMode `xml:"tds:DiscoveryMode"` +} + +type SetDiscoveryModeResponse struct { +} + +type GetRemoteDiscoveryMode struct { + XMLName string `xml:"tds:GetRemoteDiscoveryMode"` +} + +type GetRemoteDiscoveryModeResponse struct { + RemoteDiscoveryMode onvif.DiscoveryMode +} + +type SetRemoteDiscoveryMode struct { + XMLName string `xml:"tds:SetRemoteDiscoveryMode"` + RemoteDiscoveryMode onvif.DiscoveryMode `xml:"tds:RemoteDiscoveryMode"` +} + +type SetRemoteDiscoveryModeResponse struct { +} + +type GetDPAddresses struct { + XMLName string `xml:"tds:GetDPAddresses"` +} + +type GetDPAddressesResponse struct { + DPAddress onvif.NetworkHost +} + +type SetDPAddresses struct { + XMLName string `xml:"tds:SetDPAddresses"` + DPAddress onvif.NetworkHost `xml:"tds:DPAddress"` +} + +type SetDPAddressesResponse struct { +} + +type GetEndpointReference struct { + XMLName string `xml:"tds:GetEndpointReference"` +} + +type GetEndpointReferenceResponse struct { + GUID string +} + +type GetRemoteUser struct { + XMLName string `xml:"tds:GetRemoteUser"` +} + +type GetRemoteUserResponse struct { + RemoteUser onvif.RemoteUser +} + +type SetRemoteUser struct { + XMLName string `xml:"tds:SetRemoteUser"` + RemoteUser onvif.RemoteUser `xml:"tds:RemoteUser"` +} + +type SetRemoteUserResponse struct { +} + +type GetUsers struct { + XMLName string `xml:"tds:GetUsers"` +} + +type GetUsersResponse struct { + User []onvif.User +} + +// CreateUsers and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.CreateUsers +type CreateUsers struct { + XMLName string `xml:"tds:CreateUsers"` + User []onvif.UserRequest `xml:"tds:User,omitempty"` +} + +type CreateUsersResponse struct { +} + +// DeleteUsers and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.DeleteUsers +type DeleteUsers struct { + XMLName xsd.String `xml:"tds:DeleteUsers"` + Username []xsd.String `xml:"tds:Username"` +} + +type DeleteUsersResponse struct { +} + +// SetUser and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetUser +type SetUser struct { + XMLName string `xml:"tds:SetUser"` + User []onvif.UserRequest `xml:"tds:User"` +} + +type SetUserResponse struct { +} + +type GetWsdlUrl struct { + XMLName string `xml:"tds:GetWsdlUrl"` +} + +type GetWsdlUrlResponse struct { + WsdlUrl xsd.AnyURI +} + +type GetCapabilities struct { + XMLName string `xml:"tds:GetCapabilities"` + Category []onvif.CapabilityCategory `xml:"tds:Category"` +} + +type GetCapabilitiesResponse struct { + Capabilities onvif.Capabilities +} + +type GetHostname struct { + XMLName string `xml:"tds:GetHostname"` +} + +type GetHostnameResponse struct { + HostnameInformation onvif.HostnameInformation +} + +type SetHostname struct { + XMLName string `xml:"tds:SetHostname"` + Name xsd.Token `xml:"tds:Name"` +} + +type SetHostnameResponse struct { +} + +type SetHostnameFromDHCP struct { + XMLName string `xml:"tds:SetHostnameFromDHCP"` + FromDHCP xsd.Boolean `xml:"tds:FromDHCP"` +} + +type SetHostnameFromDHCPResponse struct { + RebootNeeded xsd.Boolean +} + +type GetDNS struct { + XMLName string `xml:"tds:GetDNS"` +} + +type GetDNSResponse struct { + DNSInformation onvif.DNSInformation +} + +type SetDNS struct { + XMLName string `xml:"tds:SetDNS"` + FromDHCP *xsd.Boolean `xml:"tds:FromDHCP,omitempty"` + SearchDomain *xsd.Token `xml:"tds:SearchDomain,omitempty"` + DNSManual *onvif.IPAddress `xml:"tds:DNSManual,omitempty"` +} + +type SetDNSResponse struct { +} + +type GetNTP struct { + XMLName string `xml:"tds:GetNTP"` +} + +type GetNTPResponse struct { + NTPInformation onvif.NTPInformation +} + +type SetNTP struct { + XMLName string `xml:"tds:SetNTP"` + FromDHCP xsd.Boolean `xml:"tds:FromDHCP"` + NTPManual onvif.NetworkHost `xml:"tds:NTPManual"` +} + +type SetNTPResponse struct { +} + +type GetDynamicDNS struct { + XMLName string `xml:"tds:GetDynamicDNS"` +} + +type GetDynamicDNSResponse struct { + DynamicDNSInformation onvif.DynamicDNSInformation +} + +type SetDynamicDNS struct { + XMLName string `xml:"tds:SetDynamicDNS"` + Type onvif.DynamicDNSType `xml:"tds:Type"` + Name onvif.DNSName `xml:"tds:Name"` + TTL xsd.Duration `xml:"tds:TTL"` +} + +type SetDynamicDNSResponse struct { +} + +type GetNetworkInterfaces struct { + XMLName string `xml:"tds:GetNetworkInterfaces"` +} + +type GetNetworkInterfacesResponse struct { + NetworkInterfaces onvif.NetworkInterface +} + +type SetNetworkInterfaces struct { + XMLName string `xml:"tds:SetNetworkInterfaces"` + InterfaceToken *onvif.ReferenceToken `xml:"tds:InterfaceToken,omitempty"` + NetworkInterface *onvif.NetworkInterfaceSetConfiguration `xml:"tds:NetworkInterface,omitempty"` +} + +type SetNetworkInterfacesResponse struct { + RebootNeeded xsd.Boolean +} + +type GetNetworkProtocols struct { + XMLName string `xml:"tds:GetNetworkProtocols"` +} + +type GetNetworkProtocolsResponse struct { + NetworkProtocols []onvif.NetworkProtocolResponse +} + +type SetNetworkProtocols struct { + XMLName string `xml:"tds:SetNetworkProtocols"` + NetworkProtocols []onvif.NetworkProtocolRequest `xml:"tds:NetworkProtocols"` +} + +type SetNetworkProtocolsResponse struct { +} + +type GetNetworkDefaultGateway struct { + XMLName string `xml:"tds:GetNetworkDefaultGateway"` +} + +type GetNetworkDefaultGatewayResponse struct { + NetworkGateway onvif.NetworkGateway +} + +type SetNetworkDefaultGateway struct { + XMLName string `xml:"tds:SetNetworkDefaultGateway"` + IPv4Address onvif.IPv4Address `xml:"tds:IPv4Address,omitempty"` + IPv6Address onvif.IPv6Address `xml:"tds:IPv6Address,omitempty"` +} + +type SetNetworkDefaultGatewayResponse struct { +} + +type GetZeroConfiguration struct { + XMLName string `xml:"tds:GetZeroConfiguration"` +} + +type GetZeroConfigurationResponse struct { + ZeroConfiguration onvif.NetworkZeroConfiguration +} + +type SetZeroConfiguration struct { + XMLName string `xml:"tds:SetZeroConfiguration"` + InterfaceToken onvif.ReferenceToken `xml:"tds:InterfaceToken"` + Enabled xsd.Boolean `xml:"tds:Enabled"` +} + +type SetZeroConfigurationResponse struct { +} + +type GetIPAddressFilter struct { + XMLName string `xml:"tds:GetIPAddressFilter"` +} + +type GetIPAddressFilterResponse struct { + IPAddressFilter onvif.IPAddressFilter +} + +type SetIPAddressFilter struct { + XMLName string `xml:"tds:SetIPAddressFilter"` + IPAddressFilter onvif.IPAddressFilter `xml:"tds:IPAddressFilter"` +} + +type SetIPAddressFilterResponse struct { +} + +// This operation adds an IP filter address to a device. +// If the device supports device access control based on +// IP filtering rules (denied or accepted ranges of IP addresses), +// the device shall support adding of IP filtering addresses through +// the AddIPAddressFilter command. +type AddIPAddressFilter struct { + XMLName string `xml:"tds:AddIPAddressFilter"` + IPAddressFilter onvif.IPAddressFilter `xml:"tds:IPAddressFilter"` +} + +type AddIPAddressFilterResponse struct { +} + +type RemoveIPAddressFilter struct { + XMLName string `xml:"tds:RemoveIPAddressFilter"` + IPAddressFilter onvif.IPAddressFilter `xml:"IPAddressFilter"` +} + +type RemoveIPAddressFilterResponse struct { +} + +type GetAccessPolicy struct { + XMLName string `xml:"tds:GetAccessPolicy"` +} + +type GetAccessPolicyResponse struct { + PolicyFile onvif.BinaryData +} + +type SetAccessPolicy struct { + XMLName string `xml:"tds:SetAccessPolicy"` + PolicyFile onvif.BinaryData `xml:"tds:PolicyFile"` +} + +type SetAccessPolicyResponse struct { +} + +type CreateCertificate struct { + XMLName string `xml:"tds:CreateCertificate"` + CertificateID xsd.Token `xml:"tds:CertificateID,omitempty"` + Subject string `xml:"tds:Subject,omitempty"` + ValidNotBefore xsd.DateTime `xml:"tds:ValidNotBefore,omitempty"` + ValidNotAfter xsd.DateTime `xml:"tds:ValidNotAfter,omitempty"` +} + +type CreateCertificateResponse struct { + NvtCertificate onvif.Certificate +} + +type GetCertificates struct { + XMLName string `xml:"tds:GetCertificates"` +} + +type GetCertificatesResponse struct { + NvtCertificate onvif.Certificate +} + +type GetCertificatesStatus struct { + XMLName string `xml:"tds:GetCertificatesStatus"` +} + +type GetCertificatesStatusResponse struct { + CertificateStatus onvif.CertificateStatus +} + +type SetCertificatesStatus struct { + XMLName string `xml:"tds:SetCertificatesStatus"` + CertificateStatus onvif.CertificateStatus `xml:"tds:CertificateStatus"` +} + +type SetCertificatesStatusResponse struct { +} + +// TODO: List of CertificateID +type DeleteCertificates struct { + XMLName string `xml:"tds:DeleteCertificates"` + CertificateID xsd.Token `xml:"tds:CertificateID"` +} + +type DeleteCertificatesResponse struct { +} + +// TODO: Откуда onvif:data = cid:21312413412 +type GetPkcs10Request struct { + XMLName string `xml:"tds:GetPkcs10Request"` + CertificateID xsd.Token `xml:"tds:CertificateID"` + Subject xsd.String `xml:"tds:Subject"` + Attributes onvif.BinaryData `xml:"tds:Attributes"` +} + +type GetPkcs10RequestResponse struct { + Pkcs10Request onvif.BinaryData +} + +// TODO: one or more NTVCertificate +type LoadCertificates struct { + XMLName string `xml:"tds:LoadCertificates"` + NVTCertificate onvif.Certificate `xml:"tds:NVTCertificate"` +} + +type LoadCertificatesResponse struct { +} + +type GetClientCertificateMode struct { + XMLName string `xml:"tds:GetClientCertificateMode"` +} + +type GetClientCertificateModeResponse struct { + Enabled xsd.Boolean +} + +type SetClientCertificateMode struct { + XMLName string `xml:"tds:SetClientCertificateMode"` + Enabled xsd.Boolean `xml:"tds:Enabled"` +} + +type SetClientCertificateModeResponse struct { +} + +type GetRelayOutputs struct { + XMLName string `xml:"tds:GetRelayOutputs"` +} + +type GetRelayOutputsResponse struct { + RelayOutputs onvif.RelayOutput +} + +type SetRelayOutputSettings struct { + XMLName string `xml:"tds:SetRelayOutputSettings"` + RelayOutputToken onvif.ReferenceToken `xml:"tds:RelayOutputToken"` + Properties onvif.RelayOutputSettings `xml:"tds:Properties"` +} + +type SetRelayOutputSettingsResponse struct { +} + +type SetRelayOutputState struct { + XMLName string `xml:"tds:SetRelayOutputState"` + RelayOutputToken onvif.ReferenceToken `xml:"tds:RelayOutputToken"` + LogicalState onvif.RelayLogicalState `xml:"tds:LogicalState"` +} + +type SetRelayOutputStateResponse struct { +} + +type SendAuxiliaryCommand struct { + XMLName string `xml:"tds:SendAuxiliaryCommand"` + AuxiliaryCommand onvif.AuxiliaryData `xml:"tds:AuxiliaryCommand"` +} + +type SendAuxiliaryCommandResponse struct { + AuxiliaryCommandResponse onvif.AuxiliaryData +} + +type GetCACertificates struct { + XMLName string `xml:"tds:GetCACertificates"` +} + +type GetCACertificatesResponse struct { + CACertificate onvif.Certificate +} + +// TODO: one or more CertificateWithPrivateKey +type LoadCertificateWithPrivateKey struct { + XMLName string `xml:"tds:LoadCertificateWithPrivateKey"` + CertificateWithPrivateKey onvif.CertificateWithPrivateKey `xml:"tds:CertificateWithPrivateKey"` +} + +type LoadCertificateWithPrivateKeyResponse struct { +} + +type GetCertificateInformation struct { + XMLName string `xml:"tds:GetCertificateInformation"` + CertificateID xsd.Token `xml:"tds:CertificateID"` +} + +type GetCertificateInformationResponse struct { + CertificateInformation onvif.CertificateInformation +} + +type LoadCACertificates struct { + XMLName string `xml:"tds:LoadCACertificates"` + CACertificate onvif.Certificate `xml:"tds:CACertificate"` +} + +type LoadCACertificatesResponse struct { +} + +type CreateDot1XConfiguration struct { + XMLName string `xml:"tds:CreateDot1XConfiguration"` + Dot1XConfiguration onvif.Dot1XConfiguration `xml:"tds:Dot1XConfiguration"` +} + +type CreateDot1XConfigurationResponse struct { +} + +type SetDot1XConfiguration struct { + XMLName string `xml:"tds:SetDot1XConfiguration"` + Dot1XConfiguration onvif.Dot1XConfiguration `xml:"tds:Dot1XConfiguration"` +} + +type SetDot1XConfigurationResponse struct { +} + +type GetDot1XConfiguration struct { + XMLName string `xml:"tds:GetDot1XConfiguration"` + Dot1XConfigurationToken onvif.ReferenceToken `xml:"tds:Dot1XConfigurationToken"` +} + +type GetDot1XConfigurationResponse struct { + Dot1XConfiguration onvif.Dot1XConfiguration +} + +type GetDot1XConfigurations struct { + XMLName string `xml:"tds:GetDot1XConfigurations"` +} + +type GetDot1XConfigurationsResponse struct { + Dot1XConfiguration onvif.Dot1XConfiguration +} + +// TODO: Zero or more Dot1XConfigurationToken +type DeleteDot1XConfiguration struct { + XMLName string `xml:"tds:DeleteDot1XConfiguration"` + Dot1XConfigurationToken onvif.ReferenceToken `xml:"tds:Dot1XConfigurationToken"` +} + +type DeleteDot1XConfigurationResponse struct { +} + +type GetDot11Capabilities struct { + XMLName string `xml:"tds:GetDot11Capabilities"` +} + +type GetDot11CapabilitiesResponse struct { + Capabilities onvif.Dot11Capabilities +} + +type GetDot11Status struct { + XMLName string `xml:"tds:GetDot11Status"` + InterfaceToken onvif.ReferenceToken `xml:"tds:InterfaceToken"` +} + +type GetDot11StatusResponse struct { + Status onvif.Dot11Status +} + +type ScanAvailableDot11Networks struct { + XMLName string `xml:"tds:ScanAvailableDot11Networks"` + InterfaceToken onvif.ReferenceToken `xml:"tds:InterfaceToken"` +} + +type ScanAvailableDot11NetworksResponse struct { + Networks onvif.Dot11AvailableNetworks +} + +type GetSystemUris struct { + XMLName string `xml:"tds:GetSystemUris"` +} + +type GetSystemUrisResponse struct { + SystemLogUris onvif.SystemLogUriList + SupportInfoUri xsd.AnyURI + SystemBackupUri xsd.AnyURI + Extension xsd.AnyType +} + +type StartFirmwareUpgrade struct { + XMLName string `xml:"tds:StartFirmwareUpgrade"` +} + +type StartFirmwareUpgradeResponse struct { + UploadUri xsd.AnyURI + UploadDelay xsd.Duration + ExpectedDownTime xsd.Duration +} + +type StartSystemRestore struct { + XMLName string `xml:"tds:StartSystemRestore"` +} + +type StartSystemRestoreResponse struct { + UploadUri xsd.AnyURI + ExpectedDownTime xsd.Duration +} + +type GetStorageConfigurations struct { + XMLName string `xml:"tds:GetStorageConfigurations"` +} + +type GetStorageConfigurationsResponse struct { + StorageConfigurations []StorageConfiguration +} + +type CreateStorageConfiguration struct { + XMLName string `xml:"tds:CreateStorageConfiguration"` + StorageConfiguration StorageConfigurationData `xml:"tds:StorageConfiguration"` +} + +type CreateStorageConfigurationResponse struct { + Token onvif.ReferenceToken +} + +type GetStorageConfiguration struct { + XMLName string `xml:"tds:GetStorageConfiguration"` + Token onvif.ReferenceToken `xml:"tds:Token"` +} + +type GetStorageConfigurationResponse struct { + StorageConfiguration StorageConfiguration +} + +type SetStorageConfiguration struct { + XMLName string `xml:"tds:SetStorageConfiguration"` + StorageConfiguration struct { + Token xsd.String `xml:"token,attr"` + Data StorageConfigurationData `xml:"tds:Data"` + } `xml:"tds:StorageConfiguration"` +} + +type SetStorageConfigurationResponse struct { +} + +type DeleteStorageConfiguration struct { + XMLName string `xml:"tds:DeleteStorageConfiguration"` + Token onvif.ReferenceToken `xml:"tds:Token"` +} + +type DeleteStorageConfigurationResponse struct { +} + +type GetGeoLocation struct { + XMLName string `xml:"tds:GetGeoLocation"` +} + +type GetGeoLocationResponse struct { + Location onvif.LocationEntity +} + +// TODO: one or more Location +type SetGeoLocation struct { + XMLName string `xml:"tds:SetGeoLocation"` + Location onvif.LocationEntity `xml:"tds:Location"` +} + +type SetGeoLocationResponse struct { +} + +type DeleteGeoLocation struct { + XMLName string `xml:"tds:DeleteGeoLocation"` + Location onvif.LocationEntity `xml:"tds:Location"` +} + +type DeleteGeoLocationResponse struct { +} diff --git a/digestclient.go b/digestclient.go new file mode 100644 index 0000000..b11457e --- /dev/null +++ b/digestclient.go @@ -0,0 +1,111 @@ +package onvif + +import ( + "crypto/md5" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "net/http" + "strings" +) + +// DigestClient represents an HTTP client used for making requests authenticated +// with http digest authentication. +type DigestClient struct { + client *http.Client + username string + password string + snonce string + realm string + qop string + nonceCount uint32 +} + +// NewDigestClient returns a DigestClient that wraps a given standard library http Client with the given username and password +func NewDigestClient(stdClient *http.Client, username string, password string) *DigestClient { + return &DigestClient{ + client: stdClient, + username: username, + password: password, + } +} + +func (dc *DigestClient) Do(httpMethod string, endpoint string, soap string) (*http.Response, error) { + req, err := createHttpRequest(httpMethod, endpoint, soap) + if err != nil { + return nil, err + } + if dc.snonce != "" { + req.Header.Set("Authorization", dc.getDigestAuth(req.Method, req.URL.String())) + } + + // Attempt the request using the underlying client + resp, err := dc.client.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusUnauthorized { + return resp, nil + } + + dc.getDigestParts(resp) + // We will need to return the response from another request, so defer a close on this one + defer resp.Body.Close() + + req, err = createHttpRequest(httpMethod, endpoint, soap) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", dc.getDigestAuth(req.Method, req.URL.String())) + + authedResp, err := dc.client.Do(req) + if err != nil { + return nil, err + } + return authedResp, nil +} + +func (dc *DigestClient) getDigestParts(resp *http.Response) { + result := map[string]string{} + authHeader := resp.Header.Get("WWW-Authenticate") + if len(authHeader) > 0 { + wantedHeaders := []string{"nonce", "realm", "qop"} + responseHeaders := strings.Split(authHeader, ",") + for _, r := range responseHeaders { + for _, w := range wantedHeaders { + if strings.Contains(r, w) { + result[w] = strings.Split(r, `"`)[1] + } + } + } + } + dc.snonce = result["nonce"] + dc.realm = result["realm"] + dc.qop = result["qop"] + dc.nonceCount = 0 +} + +func getMD5(text string) string { + hasher := md5.New() + hasher.Write([]byte(text)) + return hex.EncodeToString(hasher.Sum(nil)) +} + +func getCnonce() string { + b := make([]byte, 8) + io.ReadFull(rand.Reader, b) + return fmt.Sprintf("%x", b)[:16] +} + +func (dc *DigestClient) getDigestAuth(method string, uri string) string { + ha1 := getMD5(dc.username + ":" + dc.realm + ":" + dc.password) + ha2 := getMD5(method + ":" + uri) + cnonce := getCnonce() + dc.nonceCount++ + response := getMD5(fmt.Sprintf("%s:%s:%v:%s:%s:%s", ha1, dc.snonce, dc.nonceCount, cnonce, dc.qop, ha2)) + authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc="%v", qop="%s", response="%s"`, + dc.username, dc.realm, dc.snonce, uri, cnonce, dc.nonceCount, dc.qop, response) + return authorization +} diff --git a/doc.go b/doc.go index 0f47715..0bf1679 100644 --- a/doc.go +++ b/doc.go @@ -1,2 +1,2 @@ -//Package onvif is developed to provide an ONVIF client implementation on Go programming language +// Package onvif is developed to provide an ONVIF client implementation on Go programming language package onvif diff --git a/docs/Development.md b/docs/Development.md new file mode 100644 index 0000000..4d5cdaf --- /dev/null +++ b/docs/Development.md @@ -0,0 +1,34 @@ +# Development + +## Onvif Command Support +Each of the following Onvif Web services has its own directory: +- [Analytics](../analytics) +- [Device](../device) +- [Event](../event) +- [Imaging](../imaging) +- [Media](../media) +- [Media2](../media2) +- [PTZ](../ptz) + +Inside each directory there is: +- `types.go`: contains the struct definitions for each onvif command and response +- `function.go`: contains the auto-generated types that implement the `Function` interface providing `Request()` and `Response()` type mappings. + +At the root level there is: +- [names.go](../names.go): contains the auto-generated constant names of all the commands +- [mappings.go](../mappings.go): contains the auto-generated mappings for each Onvif WebService from function name to function datatype + + +### Adding support for additional commands +> **Note:** Currently, the python script looks for types that end with `Response` and work backwards from there. +> This is to prevent creating commands for every struct type defined there, and only the ones that are actually commands. +> It also skips any types ending with `FaultResponse`, as there typically are no `Fault` commands, only responses. + +For the respective web service the command belongs to, add the command and response struct definitions +into `/types.go`, and then run: +```shell +python3 python/gen_commands.py +``` + +> **Note:** You can also typically run the generator within your IDE thanks to the `//go:generate` lines +> towards the top of the `types.go` files. diff --git a/event/function.go b/event/function.go new file mode 100644 index 0000000..729bf07 --- /dev/null +++ b/event/function.go @@ -0,0 +1,99 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package event + +type CreatePullPointSubscriptionFunction struct{} + +func (_ *CreatePullPointSubscriptionFunction) Request() interface{} { + return &CreatePullPointSubscription{} +} +func (_ *CreatePullPointSubscriptionFunction) Response() interface{} { + return &CreatePullPointSubscriptionResponse{} +} + +type GetEventPropertiesFunction struct{} + +func (_ *GetEventPropertiesFunction) Request() interface{} { + return &GetEventProperties{} +} +func (_ *GetEventPropertiesFunction) Response() interface{} { + return &GetEventPropertiesResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type PullMessagesFunction struct{} + +func (_ *PullMessagesFunction) Request() interface{} { + return &PullMessages{} +} +func (_ *PullMessagesFunction) Response() interface{} { + return &PullMessagesResponse{} +} + +type RenewFunction struct{} + +func (_ *RenewFunction) Request() interface{} { + return &Renew{} +} +func (_ *RenewFunction) Response() interface{} { + return &RenewResponse{} +} + +type SeekFunction struct{} + +func (_ *SeekFunction) Request() interface{} { + return &Seek{} +} +func (_ *SeekFunction) Response() interface{} { + return &SeekResponse{} +} + +type SetSynchronizationPointFunction struct{} + +func (_ *SetSynchronizationPointFunction) Request() interface{} { + return &SetSynchronizationPoint{} +} +func (_ *SetSynchronizationPointFunction) Response() interface{} { + return &SetSynchronizationPointResponse{} +} + +type SubscribeFunction struct{} + +func (_ *SubscribeFunction) Request() interface{} { + return &Subscribe{} +} +func (_ *SubscribeFunction) Response() interface{} { + return &SubscribeResponse{} +} + +type SubscriptionReferenceFunction struct{} + +func (_ *SubscriptionReferenceFunction) Request() interface{} { + return &SubscriptionReference{} +} +func (_ *SubscriptionReferenceFunction) Response() interface{} { + return &SubscriptionReferenceResponse{} +} + +type UnsubscribeFunction struct{} + +func (_ *UnsubscribeFunction) Request() interface{} { + return &Unsubscribe{} +} +func (_ *UnsubscribeFunction) Response() interface{} { + return &UnsubscribeResponse{} +} diff --git a/event/operation.go b/event/operation.go deleted file mode 100644 index 408a2a1..0000000 --- a/event/operation.go +++ /dev/null @@ -1,130 +0,0 @@ -package event - -import ( - "github.com/kerberos-io/onvif/xsd" -) - -//GetServiceCapabilities action -type GetServiceCapabilities struct { - XMLName string `xml:"tev:GetServiceCapabilities"` -} - -//GetServiceCapabilitiesResponse type -type GetServiceCapabilitiesResponse struct { - Capabilities Capabilities -} - -//SubscriptionPolicy action -type SubscriptionPolicy struct { //tev http://www.onvif.org/ver10/events/wsdl - ChangedOnly xsd.Boolean `xml:"ChangedOnly,attr"` -} - -//Subscribe action for subscribe event topic -type Subscribe struct { //http://docs.oasis-open.org/wsn/b-2.xsd - XMLName struct{} `xml:"wsnt:Subscribe"` - ConsumerReference EndpointReferenceType `xml:"wsnt:ConsumerReference"` - Filter FilterType `xml:"wsnt:Filter"` - SubscriptionPolicy SubscriptionPolicy `xml:"wsnt:SubscriptionPolicy"` - InitialTerminationTime AbsoluteOrRelativeTimeType `xml:"wsnt:InitialTerminationTime"` -} - -//SubscribeResponse message for subscribe event topic -type SubscribeResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd - SubscriptionReference EndpointReferenceType - CurrentTime CurrentTime - TerminationTime TerminationTime -} - -//Renew action for refresh event topic subscription -type Renew struct { //http://docs.oasis-open.org/wsn/b-2.xsd - TerminationTime AbsoluteOrRelativeTimeType `xml:"wsnt:TerminationTime"` -} - -//RenewResponse for Renew action -type RenewResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd - TerminationTime TerminationTime `xml:"wsnt:TerminationTime"` - CurrentTime CurrentTime `xml:"wsnt:CurrentTime"` -} - -//Unsubscribe action for Unsubscribe event topic -type Unsubscribe struct { //http://docs.oasis-open.org/wsn/b-2.xsd - Any string -} - -//UnsubscribeResponse message for Unsubscribe event topic -type UnsubscribeResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd - Any string -} - -//CreatePullPointSubscription action -type CreatePullPointSubscription struct { - XMLName string `xml:"tev:CreatePullPointSubscription"` - Filter FilterType `xml:"tev:Filter"` - InitialTerminationTime AbsoluteOrRelativeTimeType `xml:"wsnt:InitialTerminationTime"` - SubscriptionPolicy SubscriptionPolicy `xml:"wsnt:sSubscriptionPolicy"` -} - -//CreatePullPointSubscriptionResponse action -type CreatePullPointSubscriptionResponse struct { - SubscriptionReference EndpointReferenceType - CurrentTime CurrentTime - TerminationTime TerminationTime -} - -//GetEventProperties action -type GetEventProperties struct { - XMLName string `xml:"tev:GetEventProperties"` -} - -//GetEventPropertiesResponse action -type GetEventPropertiesResponse struct { - TopicNamespaceLocation xsd.AnyURI - FixedTopicSet FixedTopicSet - TopicSet TopicSet - TopicExpressionDialect TopicExpressionDialect - MessageContentFilterDialect xsd.AnyURI - ProducerPropertiesFilterDialect xsd.AnyURI - MessageContentSchemaLocation xsd.AnyURI -} - -//Port type PullPointSubscription - -//PullMessages Action -type PullMessages struct { - XMLName string `xml:"tev:PullMessages"` - Timeout xsd.Duration `xml:"tev:Timeout"` - MessageLimit xsd.Int `xml:"tev:MessageLimit"` -} - -//PullMessagesResponse response type -type PullMessagesResponse struct { - CurrentTime CurrentTime - TerminationTime TerminationTime - NotificationMessage NotificationMessage -} - -//PullMessagesFaultResponse response type -type PullMessagesFaultResponse struct { - MaxTimeout xsd.Duration - MaxMessageLimit xsd.Int -} - -//Seek action -type Seek struct { - XMLName string `xml:"tev:Seek"` - UtcTime xsd.DateTime `xml:"tev:UtcTime"` - Reverse xsd.Boolean `xml:"tev:Reverse"` -} - -//SeekResponse action -type SeekResponse struct { -} - -//SetSynchronizationPoint action -type SetSynchronizationPoint struct { - XMLName string `xml:"tev:SetSynchronizationPoint"` -} - -//SetSynchronizationPointResponse action -type SetSynchronizationPointResponse struct { -} diff --git a/event/topic/description.go b/event/topic/description.go new file mode 100644 index 0000000..cf50664 --- /dev/null +++ b/event/topic/description.go @@ -0,0 +1,22 @@ +package topic + +import "github.com/kerberos-io/onvif/xsd" + +type MessageDescription struct { + IsProperty xsd.Boolean `xml:"IsProperty,attr"` + Source Source `json:",omitempty" xml:",omitempty"` + Data Data `json:",omitempty" xml:",omitempty"` +} + +type Source struct { + SimpleItemDescription []SimpleItemDescription `json:",omitempty" xml:",omitempty"` +} + +type Data struct { + SimpleItemDescription []SimpleItemDescription `json:",omitempty" xml:",omitempty"` +} + +type SimpleItemDescription struct { + Name xsd.AnyType `xml:"Name,attr"` + Type xsd.AnyType `xml:"Type,attr"` +} diff --git a/event/topic/ruleengine.go b/event/topic/ruleengine.go new file mode 100644 index 0000000..b6c3030 --- /dev/null +++ b/event/topic/ruleengine.go @@ -0,0 +1,63 @@ +package topic + +import "github.com/kerberos-io/onvif/xsd" + +type RuleEngine struct { + Topic *xsd.Boolean `xml:"topic,attr"` + MotionRegionDetector *MotionRegionDetector `json:",omitempty" xml:",omitempty"` + CellMotionDetector *CellMotionDetector `json:",omitempty" xml:",omitempty"` + TamperDetector *TamperDetector `json:",omitempty" xml:",omitempty"` + Recognition *Recognition `json:",omitempty" xml:",omitempty"` + CountAggregation *CountAggregation `json:",omitempty" xml:",omitempty"` +} + +type MotionRegionDetector struct { + Topic *xsd.Boolean `xml:"topic,attr"` + Motion *Motion `json:"Motion" xml:"Motion"` +} + +type CellMotionDetector struct { + Topic *xsd.Boolean `xml:"topic,attr"` + Motion *Motion +} + +type Motion struct { + Topic *xsd.Boolean `xml:"topic,attr"` + MessageDescription *MessageDescription `json:",omitempty" xml:",omitempty"` +} + +type TamperDetector struct { + Topic *xsd.Boolean `xml:"topic,attr"` + Tamper *Tamper +} + +type Tamper struct { + Topic *xsd.Boolean `xml:"topic,attr"` + MessageDescription *MessageDescription `json:",omitempty" xml:",omitempty"` +} + +type Recognition struct { + Topic *xsd.Boolean `xml:"topic,attr"` + Face *Face `json:",omitempty" xml:",omitempty"` + LicensePlate *Face `json:",omitempty" xml:",omitempty"` +} + +type Face struct { + Topic *xsd.Boolean `xml:"topic,attr"` + MessageDescription *MessageDescription `json:",omitempty" xml:",omitempty"` +} + +type LicensePlate struct { + Topic *xsd.Boolean `xml:"topic,attr"` + MessageDescription *MessageDescription `json:",omitempty" xml:",omitempty"` +} + +type CountAggregation struct { + Topic *xsd.Boolean `xml:"topic,attr"` + Counter *Counter `json:",omitempty" xml:",omitempty"` +} + +type Counter struct { + Topic *xsd.Boolean `xml:"topic,attr"` + MessageDescription *MessageDescription `json:",omitempty" xml:",omitempty"` +} diff --git a/event/type_test.go b/event/type_test.go new file mode 100644 index 0000000..a77c778 --- /dev/null +++ b/event/type_test.go @@ -0,0 +1,92 @@ +package event + +import ( + "encoding/xml" + "testing" + + "github.com/kerberos-io/onvif/xsd" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var eventPropertiesData = []byte(` + + http://www.onvif.org/onvif/ver10/topics/topicns.xml + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://www.onvif.org/ver10/tev/messageContentFilter/ItemFilter + http://www.onvif.org/onvif/ver10/schema/onvif.xsd + +`) + +var eventRenewResponse = []byte(` + + 2023-11-25T18:08:15Z + 2023-11-24T14:50:25Z + +`) + +func TestEventPropertiesUnmarshalXML(t *testing.T) { + res := GetEventPropertiesResponse{} + err := xml.Unmarshal(eventPropertiesData, &res) + require.NoError(t, err) + assert.Equal(t, FixedTopicSet(true), *res.FixedTopicSet) + assert.Equal(t, xsd.AnyURI("http://www.onvif.org/ver10/tev/messageContentFilter/ItemFilter"), *res.MessageContentFilterDialect) + assert.Equal(t, xsd.AnyURI("http://www.onvif.org/onvif/ver10/schema/onvif.xsd"), *res.MessageContentSchemaLocation) + userAlarm, exists := map[string]interface{}(*res.TopicSet)["tns1:UserAlarm"] + assert.True(t, exists) + _, exists = (userAlarm).(map[string]interface{})["tnshik:IllegalAccess"] + assert.True(t, exists) + ruleEngine, exists := map[string]interface{}(*res.TopicSet)["tns1:RuleEngine"] + assert.True(t, exists) + tamperDetector, exists := (ruleEngine).(map[string]interface{})["TamperDetector"] + assert.True(t, exists) + tamper, exists := (tamperDetector).(map[string]interface{})["Tamper"] + assert.True(t, exists) + _, exists = (tamper).(map[string]interface{})["tt:MessageDescription"] + assert.True(t, exists) +} + +func TestRenewFunction_Response(t *testing.T) { + res := RenewResponse{} + err := xml.Unmarshal(eventRenewResponse, &res) + require.NoError(t, err) + assert.NotNil(t, res.TerminationTime) + assert.NotNil(t, res.CurrentTime) + assert.Equal(t, xsd.String("2023-11-25T18:08:15Z"), *res.TerminationTime) + assert.Equal(t, xsd.String("2023-11-24T14:50:25Z"), *res.CurrentTime) +} diff --git a/event/types.go b/event/types.go index 745080b..a82e596 100644 --- a/event/types.go +++ b/event/types.go @@ -1,8 +1,15 @@ package event +//go:generate python3 ../python/gen_commands.py + import ( + "encoding/xml" + "fmt" + "reflect" + + mv "github.com/clbanning/mxj/v2" + "github.com/kerberos-io/onvif/event/topic" "github.com/kerberos-io/onvif/xsd" - "github.com/kerberos-io/onvif/xsd/onvif" ) // Address Alias @@ -10,10 +17,8 @@ type Address xsd.String // CurrentTime alias type CurrentTime xsd.DateTime //wsnt http://docs.oasis-open.org/wsn/b-2.xsd - // TerminationTime alias type TerminationTime xsd.DateTime //wsnt http://docs.oasis-open.org/wsn/b-2.xsd - // FixedTopicSet alias type FixedTopicSet xsd.Boolean //wsnt http://docs.oasis-open.org/wsn/b-2.xsd @@ -26,18 +31,6 @@ type TopicExpressionDialect xsd.AnyURI // Message alias type Message xsd.AnyType -// MessageNotification alias -type MessageNotification struct { - Message MessageNotificationHolderType -} - -type MessageNotificationHolderType struct { - UtcTime xsd.DateTime `xml:",attr"` - PropertyOperation xsd.String `xml:",attr"` - Source onvif.SimpleItem `xml:"Source>SimpleItem"` - Data onvif.SimpleItem `xml:"Data>SimpleItem"` -} - // ActionType for AttributedURIType type ActionType AttributedURIType @@ -45,22 +38,25 @@ type ActionType AttributedURIType type AttributedURIType xsd.AnyURI //wsa https://www.w3.org/2005/08/addressing/ws-addr.xsd // AbsoluteOrRelativeTimeType -type AbsoluteOrRelativeTimeType xsd.AnySimpleType //wsnt http://docs.oasis-open.org/wsn/b-2.xsd +type AbsoluteOrRelativeTimeType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd + xsd.DateTime + xsd.Duration +} // EndpointReferenceType in ws-addr type EndpointReferenceType struct { //wsa http://www.w3.org/2005/08/addressing/ws-addr.xsd - Address AttributedURIType - ReferenceParameters ReferenceParametersType - Metadata MetadataType + Address AttributedURIType `xml:"wsa:Address"` + ReferenceParameters *ReferenceParametersType + Metadata *MetadataType `xml:"Metadata"` } // FilterType struct type FilterType struct { - TopicExpression TopicExpressionType `xml:"wsnt:TopicExpression"` - MessageContent QueryExpressionType `xml:"wsnt:MessageContent"` + TopicExpression *TopicExpressionType `xml:"wsnt:TopicExpression,omitempty"` + MessageContent *QueryExpressionType `xml:"wsnt:MessageContent,omitempty"` } -// EndpointReference alais +// EndpointReference alias type EndpointReference EndpointReferenceType // ReferenceParametersType in ws-addr @@ -78,12 +74,37 @@ type MetadataType struct { //wsa https://www.w3.org/2005/08/addressing/ws-addr.x } // TopicSet alias -type TopicSet TopicSetType //wstop http://docs.oasis-open.org/wsn/t-1.xsd +type TopicSet map[string]interface{} //wstop http://docs.oasis-open.org/wsn/t-1.xsd + +type Node struct { + XMLName xml.Name + Content []byte `xml:",innerxml"` +} + +func (n *TopicSet) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + node := Node{} + err := d.DecodeElement(&node, &start) + if err != nil { + return err + } + wrapper := "root" // The TopicSet is an array, we need to wrap with a tag for XML parsing + c := fmt.Sprintf("<%s>%s", wrapper, node.Content, wrapper) + result, err := mv.NewMapXmlSeq([]byte(c)) + if err != nil { + return err + } + if result[wrapper] != nil && reflect.ValueOf(result[wrapper]).Kind() == reflect.Map { + *n = (result[wrapper]).(map[string]interface{}) + } + return nil +} // TopicSetType alias type TopicSetType struct { //wstop http://docs.oasis-open.org/wsn/t-1.xsd - ExtensibleDocumented + //ExtensibleDocumented + //here can be any element + RuleEngine *topic.RuleEngine `json:"tns:RuleEngine,omitempty" xml:",omitempty"` } // ExtensibleDocumented struct @@ -103,7 +124,30 @@ type NotificationMessageHolderType struct { SubscriptionReference SubscriptionReference //wsnt http://docs.oasis-open.org/wsn/b-2.xsd Topic Topic ProducerReference ProducerReference - Message MessageNotification + Message MessageBody +} + +type MessageBody struct { + Message MessageDescription +} + +type MessageDescription struct { + PropertyOperation xsd.AnyType `xml:"PropertyOperation,attr"` + Source Source `json:",omitempty" xml:",omitempty"` + Data Data `json:",omitempty" xml:",omitempty"` +} + +type Source struct { + SimpleItem []SimpleItem `json:",omitempty" xml:",omitempty"` +} + +type Data struct { + SimpleItem []SimpleItem `json:",omitempty" xml:",omitempty"` +} + +type SimpleItem struct { + Name xsd.AnyType `xml:"Name,attr"` + Value xsd.AnyType `xml:"Value,attr"` } // NotificationMessage Alias @@ -111,7 +155,6 @@ type NotificationMessage NotificationMessageHolderType //wsnt http://docs.oasis- // QueryExpressionType struct for wsnt:MessageContent type QueryExpressionType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd - Dialect xsd.AnyURI `xml:"Dialect,attr"` MessageKind xsd.String `xml:",chardata"` // boolean(ncex:Producer="15") } @@ -123,7 +166,6 @@ type QueryExpression QueryExpressionType // TopicExpressionType struct for wsnt:TopicExpression type TopicExpressionType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd - Dialect xsd.AnyURI `xml:"Dialect,attr"` TopicKinds xsd.String `xml:",chardata"` } @@ -187,3 +229,143 @@ type NotifyMessageNotSupportedFault struct { // SubscribeCreationFailedFault response type type SubscribeCreationFailedFault struct { } + +// GetServiceCapabilities action +type GetServiceCapabilities struct { + XMLName string `xml:"tev:GetServiceCapabilities"` +} + +// GetServiceCapabilitiesResponse type +type GetServiceCapabilitiesResponse struct { + Capabilities Capabilities +} + +// SubscriptionPolicy action +type SubscriptionPolicy struct { //tev http://www.onvif.org/ver10/events/wsdl + ChangedOnly xsd.Boolean `xml:"ChangedOnly,attr"` + string +} + +// Subscribe action for subscribe event topic +type Subscribe struct { //http://docs.oasis-open.org/wsn/b-2.xsd + XMLName struct{} `xml:"wsnt:Subscribe"` + ConsumerReference *EndpointReferenceType `xml:"wsnt:ConsumerReference"` + Filter *FilterType `xml:"wsnt:Filter"` + SubscriptionPolicy *xsd.String `xml:"wsnt:SubscriptionPolicy"` + TerminationTime *xsd.String `xml:"wsnt:TerminationTime"` +} + +// SubscribeResponse message for subscribe event topic +type SubscribeResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd + SubscriptionReference SubscriptionReferenceResponse + CurrentTime *xsd.String + TerminationTime *xsd.String +} + +// Renew action for refresh event topic subscription +type Renew struct { //http://docs.oasis-open.org/wsn/b-2.xsd + XMLName string `xml:"wsnt:Renew"` + TerminationTime xsd.String `xml:"wsnt:TerminationTime"` +} + +// RenewResponse for Renew action +type RenewResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd + TerminationTime *xsd.String + CurrentTime *xsd.String +} + +// Unsubscribe action for Unsubscribe event topic +type Unsubscribe struct { //http://docs.oasis-open.org/wsn/b-2.xsd + XMLName string `xml:"tev:Unsubscribe"` + Any string +} + +// UnsubscribeResponse message for Unsubscribe event topic +type UnsubscribeResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd + Any string +} + +// CreatePullPointSubscription action +// BUG(r) Bad AbsoluteOrRelativeTimeType type +type CreatePullPointSubscription struct { + XMLName string `xml:"tev:CreatePullPointSubscription,omitempty"` + Filter *FilterType `xml:"tev:Filter,omitempty"` + InitialTerminationTime *xsd.String `xml:"tev:InitialTerminationTime,omitempty"` + SubscriptionPolicy *xsd.String `xml:"tev:SubscriptionPolicy,omitempty"` +} + +// CreatePullPointSubscriptionResponse action +type CreatePullPointSubscriptionResponse struct { + SubscriptionReference SubscriptionReferenceResponse + CurrentTime CurrentTime + TerminationTime TerminationTime +} + +type SubscriptionReferenceResponse struct { + Address AttributedURIType + ReferenceParameters *ReferenceParametersType + Metadata *MetadataType +} + +// GetEventProperties action +type GetEventProperties struct { + XMLName string `xml:"tev:GetEventProperties"` +} + +// GetEventPropertiesResponse action +type GetEventPropertiesResponse struct { + TopicNamespaceLocation *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + FixedTopicSet *FixedTopicSet `json:",omitempty" xml:",omitempty"` + TopicSet *TopicSet `json:",omitempty" xml:",omitempty"` + TopicExpressionDialect *TopicExpressionDialect `json:",omitempty" xml:",omitempty"` + MessageContentFilterDialect *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + ProducerPropertiesFilterDialect *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + MessageContentSchemaLocation *xsd.AnyURI `json:",omitempty" xml:",omitempty"` +} + +//Port type PullPointSubscription + +// PullMessages Action +type PullMessages struct { + XMLName string `xml:"tev:PullMessages"` + Timeout xsd.Duration `xml:"tev:Timeout"` + MessageLimit xsd.Int `xml:"tev:MessageLimit"` +} + +// PullMessagesResponse response type +type PullMessagesResponse struct { + CurrentTime *xsd.String `json:",omitempty" xml:",omitempty"` + TerminationTime *xsd.String `json:",omitempty" xml:",omitempty"` + NotificationMessage []NotificationMessage `json:",omitempty" xml:",omitempty"` +} + +// PullMessagesFaultResponse response type +type PullMessagesFaultResponse struct { + MaxTimeout xsd.Duration + MaxMessageLimit xsd.Int +} + +// Seek action +type Seek struct { + XMLName string `xml:"tev:Seek"` + UtcTime xsd.DateTime `xml:"tev:UtcTime"` + Reverse xsd.Boolean `xml:"tev:Reverse"` +} + +// SeekResponse action +type SeekResponse struct { +} + +// SetSynchronizationPoint action +type SetSynchronizationPoint struct { + XMLName string `xml:"tev:SetSynchronizationPoint"` +} + +// SetSynchronizationPointResponse action +type SetSynchronizationPointResponse struct { +} + +// Notify type +type Notify struct { + NotificationMessage []NotificationMessage `json:",omitempty" xml:",omitempty"` +} diff --git a/examples/DeviceService.go b/examples/DeviceService.go index 5bf6aa8..286a820 100644 --- a/examples/DeviceService.go +++ b/examples/DeviceService.go @@ -1,14 +1,14 @@ package main import ( - "context" "fmt" + "io/ioutil" "log" "net/http" goonvif "github.com/kerberos-io/onvif" "github.com/kerberos-io/onvif/device" - sdk "github.com/kerberos-io/onvif/sdk/device" + "github.com/kerberos-io/onvif/gosoap" "github.com/kerberos-io/onvif/xsd/onvif" ) @@ -17,9 +17,15 @@ const ( password = "Supervisor" ) -func main() { - ctx := context.Background() +func readResponse(resp *http.Response) string { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + return string(b) +} +func main() { //Getting an camera instance dev, err := goonvif.NewDevice(goonvif.DeviceParams{ Xaddr: "192.168.13.14:80", @@ -32,36 +38,40 @@ func main() { } //Preparing commands + UserLevel := onvif.UserLevel("User") systemDateAndTyme := device.GetSystemDateAndTime{} - getCapabilities := device.GetCapabilities{Category: "All"} createUser := device.CreateUsers{ - User: onvif.User{ - Username: "TestUser", - Password: "TestPassword", - UserLevel: "User", + User: []onvif.UserRequest{ + { + Username: "TestUser", + Password: "TestPassword", + UserLevel: &UserLevel, + }, }, } //Commands execution - systemDateAndTymeResponse, err := sdk.Call_GetSystemDateAndTime(ctx, dev, systemDateAndTyme) + systemDateAndTymeResponse, err := dev.CallMethod(systemDateAndTyme) if err != nil { log.Println(err) } else { - fmt.Println(systemDateAndTymeResponse) + fmt.Println(readResponse(systemDateAndTymeResponse)) } - getCapabilitiesResponse, err := sdk.Call_GetCapabilities(ctx, dev, getCapabilities) + getCapabilities := device.GetCapabilities{Category: []onvif.CapabilityCategory{"All"}} + getCapabilitiesResponse, err := dev.CallMethod(getCapabilities) if err != nil { log.Println(err) } else { - fmt.Println(getCapabilitiesResponse) + fmt.Println(readResponse(getCapabilitiesResponse)) } - - createUserResponse, err := sdk.Call_CreateUsers(ctx, dev, createUser) + createUserResponse, err := dev.CallMethod(createUser) if err != nil { log.Println(err) } else { - // You could use https://github.com/kerberos-io/onvif/gosoap for pretty printing response - fmt.Println(createUserResponse) + /* + You could use https://github.com/kerberos-io/onvif/gosoap for pretty printing response + */ + fmt.Println(gosoap.SoapMessage(readResponse(createUserResponse)).StringIndent()) } } diff --git a/examples/analytic/getanalyticsconfigurations/main.go b/examples/analytic/getanalyticsconfigurations/main.go new file mode 100644 index 0000000..7dea23c --- /dev/null +++ b/examples/analytic/getanalyticsconfigurations/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/media2" + "io/ioutil" + "log" +) + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.148", // BOSCH + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + res, err := dev.CallMethod(media2.GetAnalyticsConfigurations{}) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/analytic/getprofiles/main.go b/examples/analytic/getprofiles/main.go new file mode 100644 index 0000000..f593602 --- /dev/null +++ b/examples/analytic/getprofiles/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/media2" + "io/ioutil" + "log" +) + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.148", // BOSCH + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + res, err := dev.CallMethod(media2.GetProfiles{}) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/discovery_test.go b/examples/discovery_test.go index e2bd136..b2651b0 100644 --- a/examples/discovery_test.go +++ b/examples/discovery_test.go @@ -12,12 +12,16 @@ import ( "github.com/beevik/etree" "github.com/kerberos-io/onvif" "github.com/kerberos-io/onvif/device" - discover "github.com/kerberos-io/onvif/ws-discovery" + wsdiscovery "github.com/kerberos-io/onvif/ws-discovery" ) func TestGetAvailableDevicesAtSpecificEthernetInterface(t *testing.T) { - s, err := onvif.GetAvailableDevicesAtSpecificEthernetInterface("en0") - log.Printf("%v %v", err, s) + + // client() + // runDiscovery("en0") + s, _ := wsdiscovery.GetAvailableDevicesAtSpecificEthernetInterface("en0") + + log.Printf("%v", s) } func client() { @@ -28,7 +32,7 @@ func client() { log.Printf("output %+v", dev.GetServices()) - res, err := dev.CallMethod(device.GetUsers{}) + res, _ := dev.CallMethod(device.GetUsers{}) bs, _ := ioutil.ReadAll(res.Body) log.Printf("output %+v %s", res.StatusCode, bs) } @@ -41,11 +45,7 @@ type Host struct { func runDiscovery(interfaceName string) { var hosts []*Host - devices, err := discover.SendProbe(interfaceName, nil, []string{"dn:NetworkVideoTransmitter"}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) - if err != nil { - log.Printf("error %s", err) - return - } + devices, _ := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:NetworkVideoTransmitter"}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) for _, j := range devices { doc := etree.NewDocument() if err := doc.ReadFromString(j); err != nil { diff --git a/examples/event/createpullpoint/main.go b/examples/event/createpullpoint/main.go new file mode 100644 index 0000000..cced0e6 --- /dev/null +++ b/examples/event/createpullpoint/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" + "io/ioutil" + "log" +) + +// Geovision +// Request: +// +// PT120S +// +// Response: +// +// +// http://192.168.12.149:80/onvif/events +// +// 2021-12-02T02:02:15Z +// 2021-12-02T02:04:15Z +// +// +// Test Summary: +// 1. TerminationTime = CurrentTime+InitialTerminationTime +// 2. but always return http://192.168.12.149:80/onvif/events +// 3. We can pulling the event without creating the pull point, and the unsubscribe is not supported + +// BOSCH +// Request: +// +// PT1H +// +// Response: +// +// +// http://192.168.12.148/Web_Service?Idx=1 +// +// 2021-12-02T02:59:30Z +// 2021-12-02T03:00:30Z +// +// +// Test Summary +// 1. The TerminationTime always increase one minute +// 2. The SubscriptionReference Address will change when create another PullPoint + +// === Hikvision === +// Request: +// +// PT120S +// +// Response: +// +// +// http://192.168.12.123/onvif/Events/PullSubManager_2021-12-02T06:04:46Z_0 +// +// 2021-12-02T06:04:46Z +// 2021-12-02T06:06:46Z +// +// +// Test Summary: +// 1. TerminationTime = CurrentTime+InitialTerminationTime +// 2. The pull point will drop after exceeding the termination time + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + //Xaddr: "192.168.12.148", // BOSCH + //Xaddr: "192.168.12.149", // Geovision + Xaddr: "192.168.12.123", //Hikvision + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + initialTerminationTime := xsd.String("PT120S") + res, err := dev.CallMethod(event.CreatePullPointSubscription{InitialTerminationTime: &initialTerminationTime}) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/event/eventproperties/main.go b/examples/event/eventproperties/main.go new file mode 100644 index 0000000..f4fe061 --- /dev/null +++ b/examples/event/eventproperties/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "io/ioutil" + "log" +) + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.148", // BOSCH + //Xaddr: "192.168.12.149", // Geovision + Username: "administrator", + Password: "Password1!", + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + // CreateUsers + res, err := dev.CallMethod(event.GetEventProperties{}) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/event/pullmessage/main.go b/examples/event/pullmessage/main.go new file mode 100644 index 0000000..d5d1eb5 --- /dev/null +++ b/examples/event/pullmessage/main.go @@ -0,0 +1,92 @@ +package main + +import ( + "encoding/xml" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" + "io/ioutil" + "log" +) + +// === Geovision === +// Request: +// +// PT20S +// 10 +// +// Response: +// +// 2021-12-02T02:42:30Z +// 2021-12-02T02:42:50Z +// ... +// +// +// Test Summary: +// 1. the TerminationTime = CurrentTime+Timeout +// 2. even current time exceed the TerminationTime, the pull point still alive + +// === BOSCH === +// Request: +// +// PT20S +// 10 +// +// Response: +// +// 2021-12-02T03:06:39Z +// 2021-12-02T03:07:39Z +// ... +// +// +// Test Summary: +// the TerminationTime increase one minute + +// === Hikvisiion === +// Request: +// +// Response: +// 2021-12-02T06:08:35Z +// +// 2021-12-02T06:18:40Z +// tns1:RuleEngine/CellMotionDetector/Motion +// ... +// +// +// +// Test Summary: +// +// the TerminationTime increase ten minutes +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.148", // BOSCH + //Xaddr: "192.168.12.149", // Geovision + //Xaddr: "192.168.12.123", //Hikvision + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + pullMessage := event.PullMessages{ + Timeout: xsd.Duration("PT5S"), + MessageLimit: 10, + } + + endPoint := "http://192.168.12.148/Web_Service?Idx=0" // BOSCH + //endPoint := "http://192.168.12.149:80/onvif/events" // Geovision + //endPoint := "http://192.168.12.123/onvif/Events/PullSubManager_2021-12-02T06:07:58Z_0" + requestBody, err := xml.Marshal(pullMessage) + if err != nil { + log.Fatalln(err) + } + res, err := dev.SendSoap(endPoint, string(requestBody)) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/event/renew/main.go b/examples/event/renew/main.go new file mode 100644 index 0000000..4d2e5c6 --- /dev/null +++ b/examples/event/renew/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "encoding/xml" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" + "io/ioutil" + "log" +) + +// === Geovision === +// Request: +// +// PT20S +// 10 +// +// Response: +// +// 2021-12-02T02:42:30Z +// 2021-12-02T02:42:50Z +// ... +// +// +// Test Summary: +// 1. the TerminationTime = CurrentTime+Timeout +// 2. even current time exceed the TerminationTime, the pull point still alive + +// === BOSCH === +// Request: +// +// +// 2021-12-03T15:50:03Z +// +// +// Response: +// +// +// 2021-12-03T15:50:03Z +// +// +// 2021-12-02T03:36:04Z +// +// +// +// Test Summary: +// 1. the response's TerminationTime equal request's TerminationTime +// 2. But the subscription still live for one minute + +// === Hikvision === +// Request: +// 2021-12-02T18:30:53Z +// Response: +// +// 2021-12-02T18:30:53Z +// 2021-12-02T06:31:57Z +// +// +// Test Summary: +// 1. the subscription's termination time will update if the request's TerminationTime greater than the curren time + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.148", // BOSCH + //Xaddr: "192.168.12.149", // Geovision + //Xaddr: "192.168.12.123", //Hikvision + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + terminationTime := xsd.String("PT120S") + renew := event.Renew{ + TerminationTime: terminationTime, + } + + endPoint := "http://192.168.12.148/Web_Service?Idx=0" // BOSCH + //endPoint := "http://192.168.12.149:80/onvif/events" // Geovision + //endPoint := "http://192.168.12.123:80/onvif/Events/SubManager__0" // Hikvision + requestBody, err := xml.Marshal(renew) + if err != nil { + log.Fatalln(err) + } + res, err := dev.SendSoap(endPoint, string(requestBody)) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/event/subscribe/main.go b/examples/event/subscribe/main.go new file mode 100644 index 0000000..69c079d --- /dev/null +++ b/examples/event/subscribe/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" + "io/ioutil" + "log" +) + +// === Geovision === +// Request: +// +// 2021-12-02T10:10:16Z +// +// Response: +// +// +// +// SOAP-ENV:Sender +// +// +// +// +// Validation constraint violation: invalid value in element 'wsnt:TerminationTime' +// +// +// +// +// Test Summary: +// 1. The time pattern 2021-12-02T10:20:15Z and PT1H not work and always return error +// 2. If not provide the time, camera return the error: ' fail to CallMethod: Post "http://192.168.12.149/onvif/events": net/http: HTTP/1.x transport connection broken: unexpected EOF' +// 3. Geovision might not support the BaseNotification + +// === BOSCH === +// Request: +// +// +// http://192.168.12.112:8080/ping +// +// +// 2021-12-01T15:50:03Z +// +// +// Response: +// +// +// http://192.168.12.148/Web_Service?Idx=1 +// +// 2021-12-02T03:21:13Z +// 2021-12-02T03:22:13Z +// +// +// Test Summary +// 1. The TerminationTime always increase one minute +// 2. The SubscriptionReference Address will change when create another PullPoint + +// Hikvision +// Request: +// +// +// http://192.168.12.112:8080/ping +// +// 2021-12-03T15:50:03Z +// +// Response: +// +// +// http:///onvif/Events/SubManager_2021-12-02T06:21:11Z_1 +// +// 2021-12-02T06:21:11Z +// 2021-12-02T06:22:11Z +// +// +// Test Summary +// 1. Regardless of the request's TerminationTime, the response TerminationTime always increase one minute +// 2. The SubscriptionReference Address will change when create another PullPoint +// 3. The response's reference address is invalid, renew request will fail + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.148", // BOSCH + //Xaddr: "192.168.12.149", // Geovision + //Xaddr: "192.168.12.123", //Hikvision + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + consumerAddress := event.AttributedURIType("http://192.168.12.112:8080/ping") + //terminationTime:= xsd.String("2021-12-03T15:50:03Z") + terminationTime := xsd.String("PT180S") + res, err := dev.CallMethod(event.Subscribe{ + ConsumerReference: &event.EndpointReferenceType{ + Address: consumerAddress, + }, + TerminationTime: &terminationTime, + }, + ) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + log.Printf(">> Status Code: %+v \n", res.StatusCode) + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/event/unsubscribe/main.go b/examples/event/unsubscribe/main.go new file mode 100644 index 0000000..73d6f7b --- /dev/null +++ b/examples/event/unsubscribe/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "encoding/xml" + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "io/ioutil" + "log" +) + +// === Geovision === +// Request: +// +// Response: +// +// +// SOAP-ENV:Sender +// +// +// +// Method 'tev:Unsubscribe' not implemented: method name or namespace not recognized +// +// +// +// +// Test Summary: Geovision might not support unsubscribe + +// === BOSCH === +// Request: +// +// Response: +// +// SOAP-ENV:Receiverter:Action +// Action Failed +// http://www.w3.org/2003/05/soap-envelope/node/ultimateReceiverhttp://www.w3.org/2003/05/soap-envelope/node/ultimateReceiver +// +// +// Test Summary: BOSCH might not support unsubscribe + +// === Hikvision +// Request: +// +// Response: +// + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + //Xaddr: "192.168.12.148", // BOSCH + //Xaddr: "192.168.12.149", // Geovision + Xaddr: "192.168.12.123", //Hikvision + Username: "administrator", + Password: "Password1!", + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + + unsubscribe := event.Unsubscribe{} + + //endPoint:= "http://192.168.12.148/Web_Service?Idx=0" // BOSCH + //endPoint := "http://192.168.12.149:80/onvif/events" // Geovision + endPoint := "http://192.168.12.123/onvif/Events/PullSubManager_2021-12-02T06:13:45Z_0" // Hikvision + requestBody, err := xml.Marshal(unsubscribe) + if err != nil { + log.Fatalln(err) + } + res, err := dev.SendSoap(endPoint, string(requestBody)) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/examples/getusers/main.go b/examples/getusers/main.go new file mode 100644 index 0000000..7b58084 --- /dev/null +++ b/examples/getusers/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/device" + "io/ioutil" + "log" +) + +func main() { + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: "192.168.12.149", + Username: "administrator", + Password: "Password1!", + }) + if err != nil { + log.Fatalln("fail to new device:", err) + } + // CreateUsers + res, err := dev.CallMethod(device.GetUsers{}) + if err != nil { + log.Fatalln("fail to CallMethod:", err) + } + bs, _ := ioutil.ReadAll(res.Body) + + log.Printf(">> Result: %+v \n %s", res.StatusCode, bs) +} diff --git a/functionmap.go b/functionmap.go new file mode 100644 index 0000000..49718a2 --- /dev/null +++ b/functionmap.go @@ -0,0 +1,36 @@ +package onvif + +import ( + "fmt" +) + +func FunctionByServiceAndFunctionName(serviceName, functionName string) (Function, error) { + var functionMap map[string]Function + + switch serviceName { + case DeviceWebService: + functionMap = DeviceFunctionMap + case MediaWebService: + functionMap = MediaFunctionMap + case Media2WebService: + functionMap = Media2FunctionMap + case PTZWebService: + functionMap = PTZFunctionMap + case EventWebService: + functionMap = EventFunctionMap + case AnalyticsWebService: + functionMap = AnalyticsFunctionMap + case ImagingWebService: + functionMap = ImagingFunctionMap + case RecordingWebService: + functionMap = RecordingFunctionMap + default: + return nil, fmt.Errorf("the web service '%s' is not supported", serviceName) + } + + if function, found := functionMap[functionName]; !found { + return nil, fmt.Errorf("the web service '%s' does not support the function '%s'", serviceName, functionName) + } else { + return function, nil + } +} diff --git a/go.mod b/go.mod index 90e204e..f981751 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,41 @@ module github.com/kerberos-io/onvif -go 1.15 +go 1.20 require ( - github.com/beevik/etree v1.1.0 + github.com/beevik/etree v1.2.0 + github.com/clbanning/mxj/v2 v2.7.0 github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae - github.com/gin-gonic/gin v1.7.0 - github.com/gofrs/uuid v3.2.0+incompatible - github.com/juju/errors v0.0.0-20220331221717-b38fca44723b - github.com/rs/zerolog v1.26.1 - golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d + github.com/gin-gonic/gin v1.9.1 + github.com/google/uuid v1.4.0 + github.com/stretchr/testify v1.8.4 + golang.org/x/net v0.19.0 +) + +require ( + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4a3f255..5207087 100644 --- a/go.sum +++ b/go.sum @@ -1,97 +1,94 @@ -github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= -github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw= +github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae h1:3KvK2DmA7TxQ6PZ2f0rWbdqjgJhRcqgbY70bBeE4clI= github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae/go.mod h1:wruC5r2gHdr/JIUs5Rr1V45YtsAzKXZxAnn/5rPC97g= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/juju/errors v0.0.0-20220331221717-b38fca44723b h1:AxFeSQJfcm2O3ov1wqAkTKYFsnMw2g1B4PkYujfAdkY= -github.com/juju/errors v0.0.0-20220331221717-b38fca44723b/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= -github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/gosoap/envelope.go b/gosoap/envelope.go new file mode 100644 index 0000000..3029cd6 --- /dev/null +++ b/gosoap/envelope.go @@ -0,0 +1,121 @@ +package gosoap + +import ( + "encoding/xml" + "fmt" +) + +type SOAPEnvelope struct { + XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"` + Header SOAPHeader + Body SOAPBody +} + +type SOAPHeader struct { + XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Header"` + + Headers []interface{} +} + +type SOAPBody struct { + XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Body"` + + Fault *SOAPFault `xml:",omitempty"` + Content interface{} `xml:",omitempty"` +} + +type SOAPFault struct { + XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Fault"` + + Code SOAPFaultCode `xml:",omitempty"` + Reason SOAPFaultReason `xml:",omitempty"` + Detail SOAPFaultDetail `xml:",omitempty"` +} + +// UnmarshalXML the response body +// https://github.com/faceterteam/onvif4go/blob/master/soap/types.go#L46 +// https://play.golang.org/p/FRzdAFrXZ1 +func (b *SOAPBody) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error { + if b.Content == nil { + return xml.UnmarshalError("Content must be a pointer to a struct") + } + + var ( + token xml.Token + err error + consumed bool + ) + +Loop: + for { + if token, err = d.Token(); err != nil { + return err + } + + if token == nil { + break + } + + switch se := token.(type) { + case xml.StartElement: + if consumed { + return xml.UnmarshalError("Found multiple elements inside SOAP body; not wrapped-document/literal WS-I compliant") + } else if se.Name.Space == "http://www.w3.org/2003/05/soap-envelope" && se.Name.Local == "Fault" { + b.Fault = &SOAPFault{} + b.Content = nil + + err = d.DecodeElement(b.Fault, &se) + if err != nil { + return err + } + + consumed = true + } else { + if err = d.DecodeElement(b.Content, &se); err != nil { + return err + } + + consumed = true + } + case xml.EndElement: + break Loop + } + } + + return nil +} + +type SOAPFaultCode struct { + Value string `xml:"Value"` + Subcode SOAPFaultSubCode `xml:"Subcode,omitempty"` +} + +type SOAPFaultSubCode struct { + Value string `xml:"Value"` + Subcode *SOAPFaultSubCode `xml:"Subcode,omitempty"` +} + +type SOAPFaultReason struct { + Text string `xml:"Text"` +} + +type SOAPFaultDetail struct { + Text string `xml:"Text"` +} + +func (fault *SOAPFault) String() string { + msg := fmt.Sprintf("fault reason: %s, fault detail: %s, fault code: %v %v ", + fault.Reason.Text, fault.Detail.Text, fault.Code.Value, fault.Code.Subcode.Value) + if fault.Code.Subcode.Subcode != nil { + msg += fault.Code.Subcode.Subcode.Value + } + return msg +} + +func NewSOAPEnvelope(content interface{}) *SOAPEnvelope { + return &SOAPEnvelope{ + Body: SOAPBody{ + Content: content, + }, + } +} diff --git a/gosoap/soap-builder.go b/gosoap/soap-builder.go index d3ac554..2a31d8e 100644 --- a/gosoap/soap-builder.go +++ b/gosoap/soap-builder.go @@ -81,7 +81,9 @@ func (msg *SoapMessage) AddStringBodyContent(data string) { } //doc.FindElement("./Envelope/Body").AddChild(element) bodyTag := doc.Root().SelectElement("Body") - bodyTag.AddChild(element) + if element != nil { + bodyTag.AddChild(element) + } //doc.IndentTabs() res, _ := doc.WriteToString() diff --git a/gosoap/ws-action.go b/gosoap/ws-action.go index f5a9e3d..cc9cea3 100644 --- a/gosoap/ws-action.go +++ b/gosoap/ws-action.go @@ -36,7 +36,7 @@ func NewAction(key, value string) Action { /** Generating Nonce sequence **/ auth := Action{ - // Created: time.Now().UTC().Format(time.RFC3339Nano), + // Created: time.Now().UTC().Format(time.RFC3339Nano), } return auth diff --git a/gosoap/ws-security.go b/gosoap/ws-security.go index 3821402..c56ea6b 100644 --- a/gosoap/ws-security.go +++ b/gosoap/ws-security.go @@ -9,15 +9,19 @@ import ( "github.com/elgs/gostrgen" ) -/************************* +/* +************************ + WS-Security types -*************************/ + +************************ +*/ const ( passwordType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest" encodingType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ) -//Security type :XMLName xml.Name `xml:"http://purl.org/rss/1.0/modules/content/ encoded"` +// Security type :XMLName xml.Name `xml:"http://purl.org/rss/1.0/modules/content/ encoded"` type Security struct { //XMLName xml.Name `xml:"wsse:Security"` XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd Security"` @@ -55,7 +59,7 @@ type wsAuth struct { */ -//NewSecurity get a new security +// NewSecurity get a new security func NewSecurity(username, passwd string) Security { /** Generating Nonce sequence **/ charsToGenerate := 32 @@ -81,7 +85,7 @@ func NewSecurity(username, passwd string) Security { return auth } -//Digest = B64ENCODE( SHA1( B64DECODE( Nonce ) + Date + Password ) ) +// Digest = B64ENCODE( SHA1( B64DECODE( Nonce ) + Date + Password ) ) func generateToken(Username string, Nonce string, Created string, Password string) string { sDec, _ := base64.StdEncoding.DecodeString(Nonce) diff --git a/imaging/function.go b/imaging/function.go new file mode 100644 index 0000000..b86f7f6 --- /dev/null +++ b/imaging/function.go @@ -0,0 +1,108 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package imaging + +type GetCurrentPresetFunction struct{} + +func (_ *GetCurrentPresetFunction) Request() interface{} { + return &GetCurrentPreset{} +} +func (_ *GetCurrentPresetFunction) Response() interface{} { + return &GetCurrentPresetResponse{} +} + +type GetImagingSettingsFunction struct{} + +func (_ *GetImagingSettingsFunction) Request() interface{} { + return &GetImagingSettings{} +} +func (_ *GetImagingSettingsFunction) Response() interface{} { + return &GetImagingSettingsResponse{} +} + +type GetMoveOptionsFunction struct{} + +func (_ *GetMoveOptionsFunction) Request() interface{} { + return &GetMoveOptions{} +} +func (_ *GetMoveOptionsFunction) Response() interface{} { + return &GetMoveOptionsResponse{} +} + +type GetOptionsFunction struct{} + +func (_ *GetOptionsFunction) Request() interface{} { + return &GetOptions{} +} +func (_ *GetOptionsFunction) Response() interface{} { + return &GetOptionsResponse{} +} + +type GetPresetsFunction struct{} + +func (_ *GetPresetsFunction) Request() interface{} { + return &GetPresets{} +} +func (_ *GetPresetsFunction) Response() interface{} { + return &GetPresetsResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type GetStatusFunction struct{} + +func (_ *GetStatusFunction) Request() interface{} { + return &GetStatus{} +} +func (_ *GetStatusFunction) Response() interface{} { + return &GetStatusResponse{} +} + +type MoveFunction struct{} + +func (_ *MoveFunction) Request() interface{} { + return &Move{} +} +func (_ *MoveFunction) Response() interface{} { + return &MoveResponse{} +} + +type SetCurrentPresetFunction struct{} + +func (_ *SetCurrentPresetFunction) Request() interface{} { + return &SetCurrentPreset{} +} +func (_ *SetCurrentPresetFunction) Response() interface{} { + return &SetCurrentPresetResponse{} +} + +type SetImagingSettingsFunction struct{} + +func (_ *SetImagingSettingsFunction) Request() interface{} { + return &SetImagingSettings{} +} +func (_ *SetImagingSettingsFunction) Response() interface{} { + return &SetImagingSettingsResponse{} +} + +type StopFunction struct{} + +func (_ *StopFunction) Request() interface{} { + return &Stop{} +} +func (_ *StopFunction) Response() interface{} { + return &StopResponse{} +} diff --git a/interfaces.go b/interfaces.go new file mode 100644 index 0000000..9e1c078 --- /dev/null +++ b/interfaces.go @@ -0,0 +1,6 @@ +package onvif + +type Function interface { + Request() interface{} + Response() interface{} +} diff --git a/mappings.go b/mappings.go new file mode 100644 index 0000000..1c7576c --- /dev/null +++ b/mappings.go @@ -0,0 +1,299 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package onvif + +import ( + "github.com/kerberos-io/onvif/analytics" + "github.com/kerberos-io/onvif/device" + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/imaging" + "github.com/kerberos-io/onvif/media" + "github.com/kerberos-io/onvif/media2" + "github.com/kerberos-io/onvif/ptz" + "github.com/kerberos-io/onvif/recording" +) + +var AnalyticsFunctionMap = map[string]Function{ + CreateAnalyticsModules: &analytics.CreateAnalyticsModulesFunction{}, + CreateRules: &analytics.CreateRulesFunction{}, + DeleteAnalyticsModules: &analytics.DeleteAnalyticsModulesFunction{}, + DeleteRules: &analytics.DeleteRulesFunction{}, + GetAnalyticsModuleOptions: &analytics.GetAnalyticsModuleOptionsFunction{}, + GetAnalyticsModules: &analytics.GetAnalyticsModulesFunction{}, + GetRuleOptions: &analytics.GetRuleOptionsFunction{}, + GetRules: &analytics.GetRulesFunction{}, + GetSupportedAnalyticsModules: &analytics.GetSupportedAnalyticsModulesFunction{}, + GetSupportedRules: &analytics.GetSupportedRulesFunction{}, + ModifyAnalyticsModules: &analytics.ModifyAnalyticsModulesFunction{}, + ModifyRules: &analytics.ModifyRulesFunction{}, +} + +var DeviceFunctionMap = map[string]Function{ + AddIPAddressFilter: &device.AddIPAddressFilterFunction{}, + AddScopes: &device.AddScopesFunction{}, + CreateCertificate: &device.CreateCertificateFunction{}, + CreateDot1XConfiguration: &device.CreateDot1XConfigurationFunction{}, + CreateStorageConfiguration: &device.CreateStorageConfigurationFunction{}, + CreateUsers: &device.CreateUsersFunction{}, + DeleteCertificates: &device.DeleteCertificatesFunction{}, + DeleteDot1XConfiguration: &device.DeleteDot1XConfigurationFunction{}, + DeleteGeoLocation: &device.DeleteGeoLocationFunction{}, + DeleteStorageConfiguration: &device.DeleteStorageConfigurationFunction{}, + DeleteUsers: &device.DeleteUsersFunction{}, + GetAccessPolicy: &device.GetAccessPolicyFunction{}, + GetCACertificates: &device.GetCACertificatesFunction{}, + GetCapabilities: &device.GetCapabilitiesFunction{}, + GetCertificateInformation: &device.GetCertificateInformationFunction{}, + GetCertificates: &device.GetCertificatesFunction{}, + GetCertificatesStatus: &device.GetCertificatesStatusFunction{}, + GetClientCertificateMode: &device.GetClientCertificateModeFunction{}, + GetDNS: &device.GetDNSFunction{}, + GetDPAddresses: &device.GetDPAddressesFunction{}, + GetDeviceInformation: &device.GetDeviceInformationFunction{}, + GetDiscoveryMode: &device.GetDiscoveryModeFunction{}, + GetDot11Capabilities: &device.GetDot11CapabilitiesFunction{}, + GetDot11Status: &device.GetDot11StatusFunction{}, + GetDot1XConfiguration: &device.GetDot1XConfigurationFunction{}, + GetDot1XConfigurations: &device.GetDot1XConfigurationsFunction{}, + GetDynamicDNS: &device.GetDynamicDNSFunction{}, + GetEndpointReference: &device.GetEndpointReferenceFunction{}, + GetGeoLocation: &device.GetGeoLocationFunction{}, + GetHostname: &device.GetHostnameFunction{}, + GetIPAddressFilter: &device.GetIPAddressFilterFunction{}, + GetNTP: &device.GetNTPFunction{}, + GetNetworkDefaultGateway: &device.GetNetworkDefaultGatewayFunction{}, + GetNetworkInterfaces: &device.GetNetworkInterfacesFunction{}, + GetNetworkProtocols: &device.GetNetworkProtocolsFunction{}, + GetPkcs10Request: &device.GetPkcs10RequestFunction{}, + GetRelayOutputs: &device.GetRelayOutputsFunction{}, + GetRemoteDiscoveryMode: &device.GetRemoteDiscoveryModeFunction{}, + GetRemoteUser: &device.GetRemoteUserFunction{}, + GetScopes: &device.GetScopesFunction{}, + GetServiceCapabilities: &device.GetServiceCapabilitiesFunction{}, + GetServices: &device.GetServicesFunction{}, + GetStorageConfiguration: &device.GetStorageConfigurationFunction{}, + GetStorageConfigurations: &device.GetStorageConfigurationsFunction{}, + GetSystemBackup: &device.GetSystemBackupFunction{}, + GetSystemDateAndTime: &device.GetSystemDateAndTimeFunction{}, + GetSystemLog: &device.GetSystemLogFunction{}, + GetSystemSupportInformation: &device.GetSystemSupportInformationFunction{}, + GetSystemUris: &device.GetSystemUrisFunction{}, + GetUsers: &device.GetUsersFunction{}, + GetWsdlUrl: &device.GetWsdlUrlFunction{}, + GetZeroConfiguration: &device.GetZeroConfigurationFunction{}, + LoadCACertificates: &device.LoadCACertificatesFunction{}, + LoadCertificateWithPrivateKey: &device.LoadCertificateWithPrivateKeyFunction{}, + LoadCertificates: &device.LoadCertificatesFunction{}, + RemoveIPAddressFilter: &device.RemoveIPAddressFilterFunction{}, + RemoveScopes: &device.RemoveScopesFunction{}, + RestoreSystem: &device.RestoreSystemFunction{}, + ScanAvailableDot11Networks: &device.ScanAvailableDot11NetworksFunction{}, + SendAuxiliaryCommand: &device.SendAuxiliaryCommandFunction{}, + SetAccessPolicy: &device.SetAccessPolicyFunction{}, + SetCertificatesStatus: &device.SetCertificatesStatusFunction{}, + SetClientCertificateMode: &device.SetClientCertificateModeFunction{}, + SetDNS: &device.SetDNSFunction{}, + SetDPAddresses: &device.SetDPAddressesFunction{}, + SetDiscoveryMode: &device.SetDiscoveryModeFunction{}, + SetDot1XConfiguration: &device.SetDot1XConfigurationFunction{}, + SetDynamicDNS: &device.SetDynamicDNSFunction{}, + SetGeoLocation: &device.SetGeoLocationFunction{}, + SetHostname: &device.SetHostnameFunction{}, + SetHostnameFromDHCP: &device.SetHostnameFromDHCPFunction{}, + SetIPAddressFilter: &device.SetIPAddressFilterFunction{}, + SetNTP: &device.SetNTPFunction{}, + SetNetworkDefaultGateway: &device.SetNetworkDefaultGatewayFunction{}, + SetNetworkInterfaces: &device.SetNetworkInterfacesFunction{}, + SetNetworkProtocols: &device.SetNetworkProtocolsFunction{}, + SetRelayOutputSettings: &device.SetRelayOutputSettingsFunction{}, + SetRelayOutputState: &device.SetRelayOutputStateFunction{}, + SetRemoteDiscoveryMode: &device.SetRemoteDiscoveryModeFunction{}, + SetRemoteUser: &device.SetRemoteUserFunction{}, + SetScopes: &device.SetScopesFunction{}, + SetStorageConfiguration: &device.SetStorageConfigurationFunction{}, + SetSystemDateAndTime: &device.SetSystemDateAndTimeFunction{}, + SetSystemFactoryDefault: &device.SetSystemFactoryDefaultFunction{}, + SetUser: &device.SetUserFunction{}, + SetZeroConfiguration: &device.SetZeroConfigurationFunction{}, + StartFirmwareUpgrade: &device.StartFirmwareUpgradeFunction{}, + StartSystemRestore: &device.StartSystemRestoreFunction{}, + SystemReboot: &device.SystemRebootFunction{}, + UpgradeSystemFirmware: &device.UpgradeSystemFirmwareFunction{}, +} + +var EventFunctionMap = map[string]Function{ + CreatePullPointSubscription: &event.CreatePullPointSubscriptionFunction{}, + GetEventProperties: &event.GetEventPropertiesFunction{}, + GetServiceCapabilities: &event.GetServiceCapabilitiesFunction{}, + PullMessages: &event.PullMessagesFunction{}, + Renew: &event.RenewFunction{}, + Seek: &event.SeekFunction{}, + SetSynchronizationPoint: &event.SetSynchronizationPointFunction{}, + Subscribe: &event.SubscribeFunction{}, + SubscriptionReference: &event.SubscriptionReferenceFunction{}, + Unsubscribe: &event.UnsubscribeFunction{}, +} + +var ImagingFunctionMap = map[string]Function{ + GetCurrentPreset: &imaging.GetCurrentPresetFunction{}, + GetImagingSettings: &imaging.GetImagingSettingsFunction{}, + GetMoveOptions: &imaging.GetMoveOptionsFunction{}, + GetOptions: &imaging.GetOptionsFunction{}, + GetPresets: &imaging.GetPresetsFunction{}, + GetServiceCapabilities: &imaging.GetServiceCapabilitiesFunction{}, + GetStatus: &imaging.GetStatusFunction{}, + Move: &imaging.MoveFunction{}, + SetCurrentPreset: &imaging.SetCurrentPresetFunction{}, + SetImagingSettings: &imaging.SetImagingSettingsFunction{}, + Stop: &imaging.StopFunction{}, +} + +var MediaFunctionMap = map[string]Function{ + AddAudioDecoderConfiguration: &media.AddAudioDecoderConfigurationFunction{}, + AddAudioEncoderConfiguration: &media.AddAudioEncoderConfigurationFunction{}, + AddAudioOutputConfiguration: &media.AddAudioOutputConfigurationFunction{}, + AddAudioSourceConfiguration: &media.AddAudioSourceConfigurationFunction{}, + AddMetadataConfiguration: &media.AddMetadataConfigurationFunction{}, + AddPTZConfiguration: &media.AddPTZConfigurationFunction{}, + AddVideoAnalyticsConfiguration: &media.AddVideoAnalyticsConfigurationFunction{}, + AddVideoEncoderConfiguration: &media.AddVideoEncoderConfigurationFunction{}, + AddVideoSourceConfiguration: &media.AddVideoSourceConfigurationFunction{}, + CreateOSD: &media.CreateOSDFunction{}, + CreateProfile: &media.CreateProfileFunction{}, + DeleteOSD: &media.DeleteOSDFunction{}, + DeleteProfile: &media.DeleteProfileFunction{}, + GetAudioDecoderConfiguration: &media.GetAudioDecoderConfigurationFunction{}, + GetAudioDecoderConfigurationOptions: &media.GetAudioDecoderConfigurationOptionsFunction{}, + GetAudioDecoderConfigurations: &media.GetAudioDecoderConfigurationsFunction{}, + GetAudioEncoderConfiguration: &media.GetAudioEncoderConfigurationFunction{}, + GetAudioEncoderConfigurationOptions: &media.GetAudioEncoderConfigurationOptionsFunction{}, + GetAudioEncoderConfigurations: &media.GetAudioEncoderConfigurationsFunction{}, + GetAudioOutputConfiguration: &media.GetAudioOutputConfigurationFunction{}, + GetAudioOutputConfigurationOptions: &media.GetAudioOutputConfigurationOptionsFunction{}, + GetAudioOutputConfigurations: &media.GetAudioOutputConfigurationsFunction{}, + GetAudioOutputs: &media.GetAudioOutputsFunction{}, + GetAudioSourceConfiguration: &media.GetAudioSourceConfigurationFunction{}, + GetAudioSourceConfigurationOptions: &media.GetAudioSourceConfigurationOptionsFunction{}, + GetAudioSourceConfigurations: &media.GetAudioSourceConfigurationsFunction{}, + GetAudioSources: &media.GetAudioSourcesFunction{}, + GetCompatibleAudioDecoderConfigurations: &media.GetCompatibleAudioDecoderConfigurationsFunction{}, + GetCompatibleAudioEncoderConfigurations: &media.GetCompatibleAudioEncoderConfigurationsFunction{}, + GetCompatibleAudioOutputConfigurations: &media.GetCompatibleAudioOutputConfigurationsFunction{}, + GetCompatibleAudioSourceConfigurations: &media.GetCompatibleAudioSourceConfigurationsFunction{}, + GetCompatibleMetadataConfigurations: &media.GetCompatibleMetadataConfigurationsFunction{}, + GetCompatibleVideoAnalyticsConfigurations: &media.GetCompatibleVideoAnalyticsConfigurationsFunction{}, + GetCompatibleVideoEncoderConfigurations: &media.GetCompatibleVideoEncoderConfigurationsFunction{}, + GetCompatibleVideoSourceConfigurations: &media.GetCompatibleVideoSourceConfigurationsFunction{}, + GetGuaranteedNumberOfVideoEncoderInstances: &media.GetGuaranteedNumberOfVideoEncoderInstancesFunction{}, + GetMetadataConfiguration: &media.GetMetadataConfigurationFunction{}, + GetMetadataConfigurationOptions: &media.GetMetadataConfigurationOptionsFunction{}, + GetMetadataConfigurations: &media.GetMetadataConfigurationsFunction{}, + GetOSD: &media.GetOSDFunction{}, + GetOSDOptions: &media.GetOSDOptionsFunction{}, + GetOSDs: &media.GetOSDsFunction{}, + GetProfile: &media.GetProfileFunction{}, + GetProfiles: &media.GetProfilesFunction{}, + GetServiceCapabilities: &media.GetServiceCapabilitiesFunction{}, + GetSnapshotUri: &media.GetSnapshotUriFunction{}, + GetStreamUri: &media.GetStreamUriFunction{}, + GetVideoAnalyticsConfiguration: &media.GetVideoAnalyticsConfigurationFunction{}, + GetVideoAnalyticsConfigurations: &media.GetVideoAnalyticsConfigurationsFunction{}, + GetVideoEncoderConfiguration: &media.GetVideoEncoderConfigurationFunction{}, + GetVideoEncoderConfigurationOptions: &media.GetVideoEncoderConfigurationOptionsFunction{}, + GetVideoEncoderConfigurations: &media.GetVideoEncoderConfigurationsFunction{}, + GetVideoSourceConfiguration: &media.GetVideoSourceConfigurationFunction{}, + GetVideoSourceConfigurationOptions: &media.GetVideoSourceConfigurationOptionsFunction{}, + GetVideoSourceConfigurations: &media.GetVideoSourceConfigurationsFunction{}, + GetVideoSourceModes: &media.GetVideoSourceModesFunction{}, + GetVideoSources: &media.GetVideoSourcesFunction{}, + RemoveAudioDecoderConfiguration: &media.RemoveAudioDecoderConfigurationFunction{}, + RemoveAudioEncoderConfiguration: &media.RemoveAudioEncoderConfigurationFunction{}, + RemoveAudioOutputConfiguration: &media.RemoveAudioOutputConfigurationFunction{}, + RemoveAudioSourceConfiguration: &media.RemoveAudioSourceConfigurationFunction{}, + RemoveMetadataConfiguration: &media.RemoveMetadataConfigurationFunction{}, + RemovePTZConfiguration: &media.RemovePTZConfigurationFunction{}, + RemoveVideoAnalyticsConfiguration: &media.RemoveVideoAnalyticsConfigurationFunction{}, + RemoveVideoEncoderConfiguration: &media.RemoveVideoEncoderConfigurationFunction{}, + RemoveVideoSourceConfiguration: &media.RemoveVideoSourceConfigurationFunction{}, + SetAudioDecoderConfiguration: &media.SetAudioDecoderConfigurationFunction{}, + SetAudioEncoderConfiguration: &media.SetAudioEncoderConfigurationFunction{}, + SetAudioOutputConfiguration: &media.SetAudioOutputConfigurationFunction{}, + SetAudioSourceConfiguration: &media.SetAudioSourceConfigurationFunction{}, + SetMetadataConfiguration: &media.SetMetadataConfigurationFunction{}, + SetOSD: &media.SetOSDFunction{}, + SetSynchronizationPoint: &media.SetSynchronizationPointFunction{}, + SetVideoAnalyticsConfiguration: &media.SetVideoAnalyticsConfigurationFunction{}, + SetVideoEncoderConfiguration: &media.SetVideoEncoderConfigurationFunction{}, + SetVideoSourceConfiguration: &media.SetVideoSourceConfigurationFunction{}, + SetVideoSourceMode: &media.SetVideoSourceModeFunction{}, + StartMulticastStreaming: &media.StartMulticastStreamingFunction{}, + StopMulticastStreaming: &media.StopMulticastStreamingFunction{}, +} + +var Media2FunctionMap = map[string]Function{ + AddConfiguration: &media2.AddConfigurationFunction{}, + GetAnalyticsConfigurations: &media2.GetAnalyticsConfigurationsFunction{}, + GetProfiles: &media2.GetProfilesFunction{}, + RemoveConfiguration: &media2.RemoveConfigurationFunction{}, +} + +var PTZFunctionMap = map[string]Function{ + AbsoluteMove: &ptz.AbsoluteMoveFunction{}, + ContinuousMove: &ptz.ContinuousMoveFunction{}, + CreatePresetTour: &ptz.CreatePresetTourFunction{}, + GeoMove: &ptz.GeoMoveFunction{}, + GetCompatibleConfigurations: &ptz.GetCompatibleConfigurationsFunction{}, + GetConfiguration: &ptz.GetConfigurationFunction{}, + GetConfigurationOptions: &ptz.GetConfigurationOptionsFunction{}, + GetConfigurations: &ptz.GetConfigurationsFunction{}, + GetNode: &ptz.GetNodeFunction{}, + GetNodes: &ptz.GetNodesFunction{}, + GetPresetTour: &ptz.GetPresetTourFunction{}, + GetPresetTourOptions: &ptz.GetPresetTourOptionsFunction{}, + GetPresetTours: &ptz.GetPresetToursFunction{}, + GetPresets: &ptz.GetPresetsFunction{}, + GetServiceCapabilities: &ptz.GetServiceCapabilitiesFunction{}, + GetStatus: &ptz.GetStatusFunction{}, + GotoHomePosition: &ptz.GotoHomePositionFunction{}, + GotoPreset: &ptz.GotoPresetFunction{}, + ModifyPresetTour: &ptz.ModifyPresetTourFunction{}, + OperatePresetTour: &ptz.OperatePresetTourFunction{}, + RelativeMove: &ptz.RelativeMoveFunction{}, + RemovePreset: &ptz.RemovePresetFunction{}, + RemovePresetTour: &ptz.RemovePresetTourFunction{}, + SendAuxiliaryCommand: &ptz.SendAuxiliaryCommandFunction{}, + SetConfiguration: &ptz.SetConfigurationFunction{}, + SetHomePosition: &ptz.SetHomePositionFunction{}, + SetPreset: &ptz.SetPresetFunction{}, + Stop: &ptz.StopFunction{}, +} + +var RecordingFunctionMap = map[string]Function{ + CreateRecording: &recording.CreateRecordingFunction{}, + CreateRecordingJob: &recording.CreateRecordingJobFunction{}, + CreateTrack: &recording.CreateTrackFunction{}, + DeleteRecording: &recording.DeleteRecordingFunction{}, + DeleteRecordingJob: &recording.DeleteRecordingJobFunction{}, + DeleteTrack: &recording.DeleteTrackFunction{}, + ExportRecordedData: &recording.ExportRecordedDataFunction{}, + GetExportRecordedDataState: &recording.GetExportRecordedDataStateFunction{}, + GetRecordingConfiguration: &recording.GetRecordingConfigurationFunction{}, + GetRecordingJobConfiguration: &recording.GetRecordingJobConfigurationFunction{}, + GetRecordingJobState: &recording.GetRecordingJobStateFunction{}, + GetRecordingJobs: &recording.GetRecordingJobsFunction{}, + GetRecordingOptions: &recording.GetRecordingOptionsFunction{}, + GetRecordings: &recording.GetRecordingsFunction{}, + GetServiceCapabilities: &recording.GetServiceCapabilitiesFunction{}, + GetTrackConfiguration: &recording.GetTrackConfigurationFunction{}, + SetRecordingConfiguration: &recording.SetRecordingConfigurationFunction{}, + SetRecordingJobConfiguration: &recording.SetRecordingJobConfigurationFunction{}, + SetRecordingJobMode: &recording.SetRecordingJobModeFunction{}, + SetTrackConfiguration: &recording.SetTrackConfigurationFunction{}, + StopExportRecordedData: &recording.StopExportRecordedDataFunction{}, +} diff --git a/media/function.go b/media/function.go new file mode 100644 index 0000000..87104b1 --- /dev/null +++ b/media/function.go @@ -0,0 +1,720 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package media + +type AddAudioDecoderConfigurationFunction struct{} + +func (_ *AddAudioDecoderConfigurationFunction) Request() interface{} { + return &AddAudioDecoderConfiguration{} +} +func (_ *AddAudioDecoderConfigurationFunction) Response() interface{} { + return &AddAudioDecoderConfigurationResponse{} +} + +type AddAudioEncoderConfigurationFunction struct{} + +func (_ *AddAudioEncoderConfigurationFunction) Request() interface{} { + return &AddAudioEncoderConfiguration{} +} +func (_ *AddAudioEncoderConfigurationFunction) Response() interface{} { + return &AddAudioEncoderConfigurationResponse{} +} + +type AddAudioOutputConfigurationFunction struct{} + +func (_ *AddAudioOutputConfigurationFunction) Request() interface{} { + return &AddAudioOutputConfiguration{} +} +func (_ *AddAudioOutputConfigurationFunction) Response() interface{} { + return &AddAudioOutputConfigurationResponse{} +} + +type AddAudioSourceConfigurationFunction struct{} + +func (_ *AddAudioSourceConfigurationFunction) Request() interface{} { + return &AddAudioSourceConfiguration{} +} +func (_ *AddAudioSourceConfigurationFunction) Response() interface{} { + return &AddAudioSourceConfigurationResponse{} +} + +type AddMetadataConfigurationFunction struct{} + +func (_ *AddMetadataConfigurationFunction) Request() interface{} { + return &AddMetadataConfiguration{} +} +func (_ *AddMetadataConfigurationFunction) Response() interface{} { + return &AddMetadataConfigurationResponse{} +} + +type AddPTZConfigurationFunction struct{} + +func (_ *AddPTZConfigurationFunction) Request() interface{} { + return &AddPTZConfiguration{} +} +func (_ *AddPTZConfigurationFunction) Response() interface{} { + return &AddPTZConfigurationResponse{} +} + +type AddVideoAnalyticsConfigurationFunction struct{} + +func (_ *AddVideoAnalyticsConfigurationFunction) Request() interface{} { + return &AddVideoAnalyticsConfiguration{} +} +func (_ *AddVideoAnalyticsConfigurationFunction) Response() interface{} { + return &AddVideoAnalyticsConfigurationResponse{} +} + +type AddVideoEncoderConfigurationFunction struct{} + +func (_ *AddVideoEncoderConfigurationFunction) Request() interface{} { + return &AddVideoEncoderConfiguration{} +} +func (_ *AddVideoEncoderConfigurationFunction) Response() interface{} { + return &AddVideoEncoderConfigurationResponse{} +} + +type AddVideoSourceConfigurationFunction struct{} + +func (_ *AddVideoSourceConfigurationFunction) Request() interface{} { + return &AddVideoSourceConfiguration{} +} +func (_ *AddVideoSourceConfigurationFunction) Response() interface{} { + return &AddVideoSourceConfigurationResponse{} +} + +type CreateOSDFunction struct{} + +func (_ *CreateOSDFunction) Request() interface{} { + return &CreateOSD{} +} +func (_ *CreateOSDFunction) Response() interface{} { + return &CreateOSDResponse{} +} + +type CreateProfileFunction struct{} + +func (_ *CreateProfileFunction) Request() interface{} { + return &CreateProfile{} +} +func (_ *CreateProfileFunction) Response() interface{} { + return &CreateProfileResponse{} +} + +type DeleteOSDFunction struct{} + +func (_ *DeleteOSDFunction) Request() interface{} { + return &DeleteOSD{} +} +func (_ *DeleteOSDFunction) Response() interface{} { + return &DeleteOSDResponse{} +} + +type DeleteProfileFunction struct{} + +func (_ *DeleteProfileFunction) Request() interface{} { + return &DeleteProfile{} +} +func (_ *DeleteProfileFunction) Response() interface{} { + return &DeleteProfileResponse{} +} + +type GetAudioDecoderConfigurationFunction struct{} + +func (_ *GetAudioDecoderConfigurationFunction) Request() interface{} { + return &GetAudioDecoderConfiguration{} +} +func (_ *GetAudioDecoderConfigurationFunction) Response() interface{} { + return &GetAudioDecoderConfigurationResponse{} +} + +type GetAudioDecoderConfigurationOptionsFunction struct{} + +func (_ *GetAudioDecoderConfigurationOptionsFunction) Request() interface{} { + return &GetAudioDecoderConfigurationOptions{} +} +func (_ *GetAudioDecoderConfigurationOptionsFunction) Response() interface{} { + return &GetAudioDecoderConfigurationOptionsResponse{} +} + +type GetAudioDecoderConfigurationsFunction struct{} + +func (_ *GetAudioDecoderConfigurationsFunction) Request() interface{} { + return &GetAudioDecoderConfigurations{} +} +func (_ *GetAudioDecoderConfigurationsFunction) Response() interface{} { + return &GetAudioDecoderConfigurationsResponse{} +} + +type GetAudioEncoderConfigurationFunction struct{} + +func (_ *GetAudioEncoderConfigurationFunction) Request() interface{} { + return &GetAudioEncoderConfiguration{} +} +func (_ *GetAudioEncoderConfigurationFunction) Response() interface{} { + return &GetAudioEncoderConfigurationResponse{} +} + +type GetAudioEncoderConfigurationOptionsFunction struct{} + +func (_ *GetAudioEncoderConfigurationOptionsFunction) Request() interface{} { + return &GetAudioEncoderConfigurationOptions{} +} +func (_ *GetAudioEncoderConfigurationOptionsFunction) Response() interface{} { + return &GetAudioEncoderConfigurationOptionsResponse{} +} + +type GetAudioEncoderConfigurationsFunction struct{} + +func (_ *GetAudioEncoderConfigurationsFunction) Request() interface{} { + return &GetAudioEncoderConfigurations{} +} +func (_ *GetAudioEncoderConfigurationsFunction) Response() interface{} { + return &GetAudioEncoderConfigurationsResponse{} +} + +type GetAudioOutputConfigurationFunction struct{} + +func (_ *GetAudioOutputConfigurationFunction) Request() interface{} { + return &GetAudioOutputConfiguration{} +} +func (_ *GetAudioOutputConfigurationFunction) Response() interface{} { + return &GetAudioOutputConfigurationResponse{} +} + +type GetAudioOutputConfigurationOptionsFunction struct{} + +func (_ *GetAudioOutputConfigurationOptionsFunction) Request() interface{} { + return &GetAudioOutputConfigurationOptions{} +} +func (_ *GetAudioOutputConfigurationOptionsFunction) Response() interface{} { + return &GetAudioOutputConfigurationOptionsResponse{} +} + +type GetAudioOutputConfigurationsFunction struct{} + +func (_ *GetAudioOutputConfigurationsFunction) Request() interface{} { + return &GetAudioOutputConfigurations{} +} +func (_ *GetAudioOutputConfigurationsFunction) Response() interface{} { + return &GetAudioOutputConfigurationsResponse{} +} + +type GetAudioOutputsFunction struct{} + +func (_ *GetAudioOutputsFunction) Request() interface{} { + return &GetAudioOutputs{} +} +func (_ *GetAudioOutputsFunction) Response() interface{} { + return &GetAudioOutputsResponse{} +} + +type GetAudioSourceConfigurationFunction struct{} + +func (_ *GetAudioSourceConfigurationFunction) Request() interface{} { + return &GetAudioSourceConfiguration{} +} +func (_ *GetAudioSourceConfigurationFunction) Response() interface{} { + return &GetAudioSourceConfigurationResponse{} +} + +type GetAudioSourceConfigurationOptionsFunction struct{} + +func (_ *GetAudioSourceConfigurationOptionsFunction) Request() interface{} { + return &GetAudioSourceConfigurationOptions{} +} +func (_ *GetAudioSourceConfigurationOptionsFunction) Response() interface{} { + return &GetAudioSourceConfigurationOptionsResponse{} +} + +type GetAudioSourceConfigurationsFunction struct{} + +func (_ *GetAudioSourceConfigurationsFunction) Request() interface{} { + return &GetAudioSourceConfigurations{} +} +func (_ *GetAudioSourceConfigurationsFunction) Response() interface{} { + return &GetAudioSourceConfigurationsResponse{} +} + +type GetAudioSourcesFunction struct{} + +func (_ *GetAudioSourcesFunction) Request() interface{} { + return &GetAudioSources{} +} +func (_ *GetAudioSourcesFunction) Response() interface{} { + return &GetAudioSourcesResponse{} +} + +type GetCompatibleAudioDecoderConfigurationsFunction struct{} + +func (_ *GetCompatibleAudioDecoderConfigurationsFunction) Request() interface{} { + return &GetCompatibleAudioDecoderConfigurations{} +} +func (_ *GetCompatibleAudioDecoderConfigurationsFunction) Response() interface{} { + return &GetCompatibleAudioDecoderConfigurationsResponse{} +} + +type GetCompatibleAudioEncoderConfigurationsFunction struct{} + +func (_ *GetCompatibleAudioEncoderConfigurationsFunction) Request() interface{} { + return &GetCompatibleAudioEncoderConfigurations{} +} +func (_ *GetCompatibleAudioEncoderConfigurationsFunction) Response() interface{} { + return &GetCompatibleAudioEncoderConfigurationsResponse{} +} + +type GetCompatibleAudioOutputConfigurationsFunction struct{} + +func (_ *GetCompatibleAudioOutputConfigurationsFunction) Request() interface{} { + return &GetCompatibleAudioOutputConfigurations{} +} +func (_ *GetCompatibleAudioOutputConfigurationsFunction) Response() interface{} { + return &GetCompatibleAudioOutputConfigurationsResponse{} +} + +type GetCompatibleAudioSourceConfigurationsFunction struct{} + +func (_ *GetCompatibleAudioSourceConfigurationsFunction) Request() interface{} { + return &GetCompatibleAudioSourceConfigurations{} +} +func (_ *GetCompatibleAudioSourceConfigurationsFunction) Response() interface{} { + return &GetCompatibleAudioSourceConfigurationsResponse{} +} + +type GetCompatibleMetadataConfigurationsFunction struct{} + +func (_ *GetCompatibleMetadataConfigurationsFunction) Request() interface{} { + return &GetCompatibleMetadataConfigurations{} +} +func (_ *GetCompatibleMetadataConfigurationsFunction) Response() interface{} { + return &GetCompatibleMetadataConfigurationsResponse{} +} + +type GetCompatibleVideoAnalyticsConfigurationsFunction struct{} + +func (_ *GetCompatibleVideoAnalyticsConfigurationsFunction) Request() interface{} { + return &GetCompatibleVideoAnalyticsConfigurations{} +} +func (_ *GetCompatibleVideoAnalyticsConfigurationsFunction) Response() interface{} { + return &GetCompatibleVideoAnalyticsConfigurationsResponse{} +} + +type GetCompatibleVideoEncoderConfigurationsFunction struct{} + +func (_ *GetCompatibleVideoEncoderConfigurationsFunction) Request() interface{} { + return &GetCompatibleVideoEncoderConfigurations{} +} +func (_ *GetCompatibleVideoEncoderConfigurationsFunction) Response() interface{} { + return &GetCompatibleVideoEncoderConfigurationsResponse{} +} + +type GetCompatibleVideoSourceConfigurationsFunction struct{} + +func (_ *GetCompatibleVideoSourceConfigurationsFunction) Request() interface{} { + return &GetCompatibleVideoSourceConfigurations{} +} +func (_ *GetCompatibleVideoSourceConfigurationsFunction) Response() interface{} { + return &GetCompatibleVideoSourceConfigurationsResponse{} +} + +type GetGuaranteedNumberOfVideoEncoderInstancesFunction struct{} + +func (_ *GetGuaranteedNumberOfVideoEncoderInstancesFunction) Request() interface{} { + return &GetGuaranteedNumberOfVideoEncoderInstances{} +} +func (_ *GetGuaranteedNumberOfVideoEncoderInstancesFunction) Response() interface{} { + return &GetGuaranteedNumberOfVideoEncoderInstancesResponse{} +} + +type GetMetadataConfigurationFunction struct{} + +func (_ *GetMetadataConfigurationFunction) Request() interface{} { + return &GetMetadataConfiguration{} +} +func (_ *GetMetadataConfigurationFunction) Response() interface{} { + return &GetMetadataConfigurationResponse{} +} + +type GetMetadataConfigurationOptionsFunction struct{} + +func (_ *GetMetadataConfigurationOptionsFunction) Request() interface{} { + return &GetMetadataConfigurationOptions{} +} +func (_ *GetMetadataConfigurationOptionsFunction) Response() interface{} { + return &GetMetadataConfigurationOptionsResponse{} +} + +type GetMetadataConfigurationsFunction struct{} + +func (_ *GetMetadataConfigurationsFunction) Request() interface{} { + return &GetMetadataConfigurations{} +} +func (_ *GetMetadataConfigurationsFunction) Response() interface{} { + return &GetMetadataConfigurationsResponse{} +} + +type GetOSDFunction struct{} + +func (_ *GetOSDFunction) Request() interface{} { + return &GetOSD{} +} +func (_ *GetOSDFunction) Response() interface{} { + return &GetOSDResponse{} +} + +type GetOSDOptionsFunction struct{} + +func (_ *GetOSDOptionsFunction) Request() interface{} { + return &GetOSDOptions{} +} +func (_ *GetOSDOptionsFunction) Response() interface{} { + return &GetOSDOptionsResponse{} +} + +type GetOSDsFunction struct{} + +func (_ *GetOSDsFunction) Request() interface{} { + return &GetOSDs{} +} +func (_ *GetOSDsFunction) Response() interface{} { + return &GetOSDsResponse{} +} + +type GetProfileFunction struct{} + +func (_ *GetProfileFunction) Request() interface{} { + return &GetProfile{} +} +func (_ *GetProfileFunction) Response() interface{} { + return &GetProfileResponse{} +} + +type GetProfilesFunction struct{} + +func (_ *GetProfilesFunction) Request() interface{} { + return &GetProfiles{} +} +func (_ *GetProfilesFunction) Response() interface{} { + return &GetProfilesResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type GetSnapshotUriFunction struct{} + +func (_ *GetSnapshotUriFunction) Request() interface{} { + return &GetSnapshotUri{} +} +func (_ *GetSnapshotUriFunction) Response() interface{} { + return &GetSnapshotUriResponse{} +} + +type GetStreamUriFunction struct{} + +func (_ *GetStreamUriFunction) Request() interface{} { + return &GetStreamUri{} +} +func (_ *GetStreamUriFunction) Response() interface{} { + return &GetStreamUriResponse{} +} + +type GetVideoAnalyticsConfigurationFunction struct{} + +func (_ *GetVideoAnalyticsConfigurationFunction) Request() interface{} { + return &GetVideoAnalyticsConfiguration{} +} +func (_ *GetVideoAnalyticsConfigurationFunction) Response() interface{} { + return &GetVideoAnalyticsConfigurationResponse{} +} + +type GetVideoAnalyticsConfigurationsFunction struct{} + +func (_ *GetVideoAnalyticsConfigurationsFunction) Request() interface{} { + return &GetVideoAnalyticsConfigurations{} +} +func (_ *GetVideoAnalyticsConfigurationsFunction) Response() interface{} { + return &GetVideoAnalyticsConfigurationsResponse{} +} + +type GetVideoEncoderConfigurationFunction struct{} + +func (_ *GetVideoEncoderConfigurationFunction) Request() interface{} { + return &GetVideoEncoderConfiguration{} +} +func (_ *GetVideoEncoderConfigurationFunction) Response() interface{} { + return &GetVideoEncoderConfigurationResponse{} +} + +type GetVideoEncoderConfigurationOptionsFunction struct{} + +func (_ *GetVideoEncoderConfigurationOptionsFunction) Request() interface{} { + return &GetVideoEncoderConfigurationOptions{} +} +func (_ *GetVideoEncoderConfigurationOptionsFunction) Response() interface{} { + return &GetVideoEncoderConfigurationOptionsResponse{} +} + +type GetVideoEncoderConfigurationsFunction struct{} + +func (_ *GetVideoEncoderConfigurationsFunction) Request() interface{} { + return &GetVideoEncoderConfigurations{} +} +func (_ *GetVideoEncoderConfigurationsFunction) Response() interface{} { + return &GetVideoEncoderConfigurationsResponse{} +} + +type GetVideoSourceConfigurationFunction struct{} + +func (_ *GetVideoSourceConfigurationFunction) Request() interface{} { + return &GetVideoSourceConfiguration{} +} +func (_ *GetVideoSourceConfigurationFunction) Response() interface{} { + return &GetVideoSourceConfigurationResponse{} +} + +type GetVideoSourceConfigurationOptionsFunction struct{} + +func (_ *GetVideoSourceConfigurationOptionsFunction) Request() interface{} { + return &GetVideoSourceConfigurationOptions{} +} +func (_ *GetVideoSourceConfigurationOptionsFunction) Response() interface{} { + return &GetVideoSourceConfigurationOptionsResponse{} +} + +type GetVideoSourceConfigurationsFunction struct{} + +func (_ *GetVideoSourceConfigurationsFunction) Request() interface{} { + return &GetVideoSourceConfigurations{} +} +func (_ *GetVideoSourceConfigurationsFunction) Response() interface{} { + return &GetVideoSourceConfigurationsResponse{} +} + +type GetVideoSourceModesFunction struct{} + +func (_ *GetVideoSourceModesFunction) Request() interface{} { + return &GetVideoSourceModes{} +} +func (_ *GetVideoSourceModesFunction) Response() interface{} { + return &GetVideoSourceModesResponse{} +} + +type GetVideoSourcesFunction struct{} + +func (_ *GetVideoSourcesFunction) Request() interface{} { + return &GetVideoSources{} +} +func (_ *GetVideoSourcesFunction) Response() interface{} { + return &GetVideoSourcesResponse{} +} + +type RemoveAudioDecoderConfigurationFunction struct{} + +func (_ *RemoveAudioDecoderConfigurationFunction) Request() interface{} { + return &RemoveAudioDecoderConfiguration{} +} +func (_ *RemoveAudioDecoderConfigurationFunction) Response() interface{} { + return &RemoveAudioDecoderConfigurationResponse{} +} + +type RemoveAudioEncoderConfigurationFunction struct{} + +func (_ *RemoveAudioEncoderConfigurationFunction) Request() interface{} { + return &RemoveAudioEncoderConfiguration{} +} +func (_ *RemoveAudioEncoderConfigurationFunction) Response() interface{} { + return &RemoveAudioEncoderConfigurationResponse{} +} + +type RemoveAudioOutputConfigurationFunction struct{} + +func (_ *RemoveAudioOutputConfigurationFunction) Request() interface{} { + return &RemoveAudioOutputConfiguration{} +} +func (_ *RemoveAudioOutputConfigurationFunction) Response() interface{} { + return &RemoveAudioOutputConfigurationResponse{} +} + +type RemoveAudioSourceConfigurationFunction struct{} + +func (_ *RemoveAudioSourceConfigurationFunction) Request() interface{} { + return &RemoveAudioSourceConfiguration{} +} +func (_ *RemoveAudioSourceConfigurationFunction) Response() interface{} { + return &RemoveAudioSourceConfigurationResponse{} +} + +type RemoveMetadataConfigurationFunction struct{} + +func (_ *RemoveMetadataConfigurationFunction) Request() interface{} { + return &RemoveMetadataConfiguration{} +} +func (_ *RemoveMetadataConfigurationFunction) Response() interface{} { + return &RemoveMetadataConfigurationResponse{} +} + +type RemovePTZConfigurationFunction struct{} + +func (_ *RemovePTZConfigurationFunction) Request() interface{} { + return &RemovePTZConfiguration{} +} +func (_ *RemovePTZConfigurationFunction) Response() interface{} { + return &RemovePTZConfigurationResponse{} +} + +type RemoveVideoAnalyticsConfigurationFunction struct{} + +func (_ *RemoveVideoAnalyticsConfigurationFunction) Request() interface{} { + return &RemoveVideoAnalyticsConfiguration{} +} +func (_ *RemoveVideoAnalyticsConfigurationFunction) Response() interface{} { + return &RemoveVideoAnalyticsConfigurationResponse{} +} + +type RemoveVideoEncoderConfigurationFunction struct{} + +func (_ *RemoveVideoEncoderConfigurationFunction) Request() interface{} { + return &RemoveVideoEncoderConfiguration{} +} +func (_ *RemoveVideoEncoderConfigurationFunction) Response() interface{} { + return &RemoveVideoEncoderConfigurationResponse{} +} + +type RemoveVideoSourceConfigurationFunction struct{} + +func (_ *RemoveVideoSourceConfigurationFunction) Request() interface{} { + return &RemoveVideoSourceConfiguration{} +} +func (_ *RemoveVideoSourceConfigurationFunction) Response() interface{} { + return &RemoveVideoSourceConfigurationResponse{} +} + +type SetAudioDecoderConfigurationFunction struct{} + +func (_ *SetAudioDecoderConfigurationFunction) Request() interface{} { + return &SetAudioDecoderConfiguration{} +} +func (_ *SetAudioDecoderConfigurationFunction) Response() interface{} { + return &SetAudioDecoderConfigurationResponse{} +} + +type SetAudioEncoderConfigurationFunction struct{} + +func (_ *SetAudioEncoderConfigurationFunction) Request() interface{} { + return &SetAudioEncoderConfiguration{} +} +func (_ *SetAudioEncoderConfigurationFunction) Response() interface{} { + return &SetAudioEncoderConfigurationResponse{} +} + +type SetAudioOutputConfigurationFunction struct{} + +func (_ *SetAudioOutputConfigurationFunction) Request() interface{} { + return &SetAudioOutputConfiguration{} +} +func (_ *SetAudioOutputConfigurationFunction) Response() interface{} { + return &SetAudioOutputConfigurationResponse{} +} + +type SetAudioSourceConfigurationFunction struct{} + +func (_ *SetAudioSourceConfigurationFunction) Request() interface{} { + return &SetAudioSourceConfiguration{} +} +func (_ *SetAudioSourceConfigurationFunction) Response() interface{} { + return &SetAudioSourceConfigurationResponse{} +} + +type SetMetadataConfigurationFunction struct{} + +func (_ *SetMetadataConfigurationFunction) Request() interface{} { + return &SetMetadataConfiguration{} +} +func (_ *SetMetadataConfigurationFunction) Response() interface{} { + return &SetMetadataConfigurationResponse{} +} + +type SetOSDFunction struct{} + +func (_ *SetOSDFunction) Request() interface{} { + return &SetOSD{} +} +func (_ *SetOSDFunction) Response() interface{} { + return &SetOSDResponse{} +} + +type SetSynchronizationPointFunction struct{} + +func (_ *SetSynchronizationPointFunction) Request() interface{} { + return &SetSynchronizationPoint{} +} +func (_ *SetSynchronizationPointFunction) Response() interface{} { + return &SetSynchronizationPointResponse{} +} + +type SetVideoAnalyticsConfigurationFunction struct{} + +func (_ *SetVideoAnalyticsConfigurationFunction) Request() interface{} { + return &SetVideoAnalyticsConfiguration{} +} +func (_ *SetVideoAnalyticsConfigurationFunction) Response() interface{} { + return &SetVideoAnalyticsConfigurationResponse{} +} + +type SetVideoEncoderConfigurationFunction struct{} + +func (_ *SetVideoEncoderConfigurationFunction) Request() interface{} { + return &SetVideoEncoderConfiguration{} +} +func (_ *SetVideoEncoderConfigurationFunction) Response() interface{} { + return &SetVideoEncoderConfigurationResponse{} +} + +type SetVideoSourceConfigurationFunction struct{} + +func (_ *SetVideoSourceConfigurationFunction) Request() interface{} { + return &SetVideoSourceConfiguration{} +} +func (_ *SetVideoSourceConfigurationFunction) Response() interface{} { + return &SetVideoSourceConfigurationResponse{} +} + +type SetVideoSourceModeFunction struct{} + +func (_ *SetVideoSourceModeFunction) Request() interface{} { + return &SetVideoSourceMode{} +} +func (_ *SetVideoSourceModeFunction) Response() interface{} { + return &SetVideoSourceModeResponse{} +} + +type StartMulticastStreamingFunction struct{} + +func (_ *StartMulticastStreamingFunction) Request() interface{} { + return &StartMulticastStreaming{} +} +func (_ *StartMulticastStreamingFunction) Response() interface{} { + return &StartMulticastStreamingResponse{} +} + +type StopMulticastStreamingFunction struct{} + +func (_ *StopMulticastStreamingFunction) Request() interface{} { + return &StopMulticastStreaming{} +} +func (_ *StopMulticastStreamingFunction) Response() interface{} { + return &StopMulticastStreamingResponse{} +} diff --git a/media/types.go b/media/types.go index 4766660..1c9258c 100644 --- a/media/types.go +++ b/media/types.go @@ -1,5 +1,7 @@ package media +//go:generate python3 ../python/gen_commands.py + import ( "github.com/kerberos-io/onvif/xsd" "github.com/kerberos-io/onvif/xsd/onvif" @@ -72,6 +74,8 @@ type CreateProfileResponse struct { Profile onvif.Profile } +// GetProfile and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetProfile type GetProfile struct { XMLName string `xml:"trt:GetProfile"` ProfileToken onvif.ReferenceToken `xml:"trt:ProfileToken"` @@ -290,6 +294,8 @@ type GetVideoAnalyticsConfigurationsResponse struct { Configurations onvif.VideoAnalyticsConfiguration } +// GetMetadataConfigurations and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetMetadataConfigurations type GetMetadataConfigurations struct { XMLName string `xml:"trt:GetMetadataConfigurations"` } @@ -323,6 +329,8 @@ type GetVideoSourceConfigurationResponse struct { Configuration onvif.VideoSourceConfiguration } +// GetVideoEncoderConfiguration and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetVideoEncoderConfiguration type GetVideoEncoderConfiguration struct { XMLName string `xml:"trt:GetVideoEncoderConfiguration"` ConfigurationToken onvif.ReferenceToken `xml:"trt:ConfigurationToken"` @@ -359,6 +367,8 @@ type GetVideoAnalyticsConfigurationResponse struct { Configuration onvif.VideoAnalyticsConfiguration } +// GetMetadataConfiguration and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetMetadataConfiguration type GetMetadataConfiguration struct { XMLName string `xml:"trt:GetMetadataConfiguration"` ConfigurationToken onvif.ReferenceToken `xml:"trt:ConfigurationToken"` @@ -431,6 +441,8 @@ type GetCompatibleVideoAnalyticsConfigurationsResponse struct { Configurations onvif.VideoAnalyticsConfiguration } +// GetCompatibleMetadataConfigurations and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetCompatibleMetadataConfigurations type GetCompatibleMetadataConfigurations struct { XMLName string `xml:"trt:GetCompatibleMetadataConfigurations"` ProfileToken onvif.ReferenceToken `xml:"trt:ProfileToken"` @@ -468,9 +480,9 @@ type SetVideoSourceConfigurationResponse struct { } type SetVideoEncoderConfiguration struct { - XMLName string `xml:"trt:SetVideoEncoderConfiguration"` - Configuration onvif.VideoEncoderConfiguration `xml:"trt:Configuration"` - ForcePersistence xsd.Boolean `xml:"trt:ForcePersistence"` + XMLName string `xml:"trt:SetVideoEncoderConfiguration"` + Configuration *onvif.VideoEncoderConfigurationRequest `xml:"trt:Configuration,omitempty"` + ForcePersistence *xsd.Boolean `xml:"trt:ForcePersistence,omitempty"` } type SetVideoEncoderConfigurationResponse struct { @@ -503,10 +515,12 @@ type SetVideoAnalyticsConfiguration struct { type SetVideoAnalyticsConfigurationResponse struct { } +// SetMetadataConfiguration and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.SetMetadataConfiguration type SetMetadataConfiguration struct { - XMLName string `xml:"trt:GetDeviceInformation"` - Configuration onvif.MetadataConfiguration `xml:"trt:Configuration"` - ForcePersistence xsd.Boolean `xml:"trt:ForcePersistence"` + XMLName string `xml:"trt:SetMetadataConfiguration"` + Configuration onvif.MetadataConfigurationRequest `xml:"trt:Configuration"` + ForcePersistence xsd.Boolean `xml:"trt:ForcePersistence"` } type SetMetadataConfigurationResponse struct { @@ -540,6 +554,8 @@ type GetVideoSourceConfigurationOptionsResponse struct { Options onvif.VideoSourceConfigurationOptions } +// GetVideoEncoderConfigurationOptions and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetVideoEncoderConfigurationOptions type GetVideoEncoderConfigurationOptions struct { XMLName string `xml:"trt:GetVideoEncoderConfigurationOptions"` ProfileToken onvif.ReferenceToken `xml:"trt:ProfileToken"` @@ -612,10 +628,12 @@ type GetGuaranteedNumberOfVideoEncoderInstancesResponse struct { MPEG4 int } +// GetStreamUri and its properties are defined in the Onvif specification: +// https://www.onvif.org/ver10/media/wsdl/media.wsdl#op.GetStreamUri type GetStreamUri struct { - XMLName string `xml:"trt:GetStreamUri"` - StreamSetup onvif.StreamSetup `xml:"trt:StreamSetup"` - ProfileToken onvif.ReferenceToken `xml:"trt:ProfileToken"` + XMLName string `xml:"trt:GetStreamUri"` + StreamSetup *onvif.StreamSetup `xml:"trt:StreamSetup"` + ProfileToken *onvif.ReferenceToken `xml:"trt:ProfileToken"` } type GetStreamUriResponse struct { diff --git a/media2/function.go b/media2/function.go new file mode 100644 index 0000000..83e7423 --- /dev/null +++ b/media2/function.go @@ -0,0 +1,45 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package media2 + +type AddConfigurationFunction struct{} + +func (_ *AddConfigurationFunction) Request() interface{} { + return &AddConfiguration{} +} +func (_ *AddConfigurationFunction) Response() interface{} { + return &AddConfigurationResponse{} +} + +type GetAnalyticsConfigurationsFunction struct{} + +func (_ *GetAnalyticsConfigurationsFunction) Request() interface{} { + return &GetAnalyticsConfigurations{} +} +func (_ *GetAnalyticsConfigurationsFunction) Response() interface{} { + return &GetAnalyticsConfigurationsResponse{} +} + +type GetProfilesFunction struct{} + +func (_ *GetProfilesFunction) Request() interface{} { + return &GetProfiles{} +} +func (_ *GetProfilesFunction) Response() interface{} { + return &GetProfilesResponse{} +} + +type RemoveConfigurationFunction struct{} + +func (_ *RemoveConfigurationFunction) Request() interface{} { + return &RemoveConfiguration{} +} +func (_ *RemoveConfigurationFunction) Response() interface{} { + return &RemoveConfigurationResponse{} +} diff --git a/media2/types.go b/media2/types.go new file mode 100644 index 0000000..178bb91 --- /dev/null +++ b/media2/types.go @@ -0,0 +1,93 @@ +package media2 + +//go:generate python3 ../python/gen_commands.py + +import ( + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" +) + +type GetProfiles struct { + XMLName string `xml:"tr2:GetProfiles"` +} + +type GetProfilesResponse struct { + Profiles []Profile +} + +type Profile struct { + Token string `xml:"token,attr"` + Fixed bool `xml:"fixed,attr"` + Name string +} + +type GetAnalyticsConfigurations struct { + XMLName string `xml:"tr2:GetAnalyticsConfigurations"` +} + +type GetAnalyticsConfigurationsResponse struct { + Configurations []Configurations +} + +type Configurations struct { + onvif.ConfigurationEntity + AnalyticsEngineConfiguration *AnalyticsEngineConfiguration `json:",omitempty"` + RuleEngineConfiguration *RuleEngineConfiguration `json:",omitempty"` +} + +type AnalyticsEngineConfiguration struct { + AnalyticsModule []AnalyticsModule +} + +type AnalyticsModule struct { + Name string `xml:",attr"` + Type string `xml:",attr"` + Parameters Parameters +} + +type RuleEngineConfiguration struct { + Rule []Rule `json:",omitempty"` +} + +type Rule struct { + Name string `xml:",attr"` + Type string `xml:",attr"` + Parameters Parameters +} + +type Parameters struct { + SimpleItem []SimpleItem `json:",omitempty"` + ElementItem []ElementItem `json:",omitempty"` +} + +type SimpleItem struct { + Name string `xml:",attr"` + Value string `xml:",attr"` +} + +type ElementItem struct { + Name string `xml:",attr"` +} + +type AddConfiguration struct { + XMLName string `xml:"tr2:AddConfiguration"` + ProfileToken string `xml:"tr2:ProfileToken"` + Name string `xml:"tr2:Name,omitempty"` + Configuration []Configuration +} + +type AddConfigurationResponse struct{} + +type RemoveConfiguration struct { + XMLName string `xml:"tr2:RemoveConfiguration"` + ProfileToken string `xml:"tr2:ProfileToken"` + Configuration []Configuration +} + +type RemoveConfigurationResponse struct{} + +type Configuration struct { + XMLName xsd.String `xml:"tr2:Configuration"` + Type *xsd.String `xml:"tr2:Type,omitempty"` + Token *xsd.String `xml:"tr2:Token,omitempty"` +} diff --git a/media2/types_test.go b/media2/types_test.go new file mode 100644 index 0000000..9db97a7 --- /dev/null +++ b/media2/types_test.go @@ -0,0 +1,134 @@ +package media2 + +import ( + "encoding/xml" + "fmt" + "testing" + + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUnmarshalGetProfilesResponse(t *testing.T) { + profile1Name := "H26x_L1S1" + profile1Token := "profile_1" + profile1Fixed := false + profile2Name := "JPEG_L1S3" + profile2Token := "profile_2" + profile2Fixed := true + GetProfilesResponseData := fmt.Sprintf(` + + %s + %s + + `, profile1Token, profile1Fixed, profile1Name, profile2Token, profile2Fixed, profile2Name) + + getProfilesResponse := &GetProfilesResponse{} + err := xml.Unmarshal([]byte(GetProfilesResponseData), getProfilesResponse) + require.NoError(t, err) + + assert.Equal(t, getProfilesResponse.Profiles[0].Token, profile1Token) + assert.Equal(t, getProfilesResponse.Profiles[0].Fixed, profile1Fixed) + assert.Equal(t, getProfilesResponse.Profiles[0].Name, profile1Name) + assert.Equal(t, getProfilesResponse.Profiles[1].Token, profile2Token) + assert.Equal(t, getProfilesResponse.Profiles[1].Fixed, profile2Fixed) + assert.Equal(t, getProfilesResponse.Profiles[1].Name, profile2Name) +} + +func TestUnmarshalGetAnalyticsConfigurationsResponse(t *testing.T) { + configToken := onvif.ReferenceToken("token_1") + configName := onvif.Name("Analytics_1") + useCount := 0 + analyticsModuleName := "Viproc" + analyticsModuleType := "tt:Viproc" + analyticsModuleItemName := "AnalysisType" + analyticsModuleItemValue := "Intelligent Video Analytics" + ruleName := "The Min ObjectHeight" + ruleType := "tt:ObjectInField" + ruleItemName := "MaxObjectHeight" + ruleItemValue := "100" + + responseData := fmt.Sprintf(` + + + %s + %d + + + + + + + + + + + + + + + + + `, configToken, configName, useCount, analyticsModuleName, analyticsModuleType, analyticsModuleItemName, analyticsModuleItemValue, + ruleName, ruleType, ruleItemName, ruleItemValue) + + response := &GetAnalyticsConfigurationsResponse{} + err := xml.Unmarshal([]byte(responseData), response) + require.NoError(t, err) + + assert.Equal(t, response.Configurations[0].Token, configToken) + assert.Equal(t, response.Configurations[0].Name, configName) + assert.Equal(t, response.Configurations[0].AnalyticsEngineConfiguration.AnalyticsModule[0].Name, analyticsModuleName) + assert.Equal(t, response.Configurations[0].AnalyticsEngineConfiguration.AnalyticsModule[0].Type, analyticsModuleType) + assert.Equal(t, response.Configurations[0].AnalyticsEngineConfiguration.AnalyticsModule[0].Parameters.SimpleItem[0].Name, analyticsModuleItemName) + assert.Equal(t, response.Configurations[0].AnalyticsEngineConfiguration.AnalyticsModule[0].Parameters.SimpleItem[0].Value, analyticsModuleItemValue) + assert.Equal(t, response.Configurations[0].RuleEngineConfiguration.Rule[0].Name, ruleName) + assert.Equal(t, response.Configurations[0].RuleEngineConfiguration.Rule[0].Type, ruleType) + assert.Equal(t, response.Configurations[0].RuleEngineConfiguration.Rule[0].Parameters.SimpleItem[0].Name, ruleItemName) + assert.Equal(t, response.Configurations[0].RuleEngineConfiguration.Rule[0].Parameters.SimpleItem[0].Value, ruleItemValue) +} + +func TestMarshalAddConfigurationRequest(t *testing.T) { + analyticsType := xsd.String("Analytics") + analyticsToken := xsd.String("AnalyticsToken") + request := AddConfiguration{ + ProfileToken: "profile_1", + Configuration: []Configuration{ + { + Type: &analyticsType, + Token: &analyticsToken, + }, + }, + } + expected := fmt.Sprintf("%s%s%s", + request.ProfileToken, *request.Configuration[0].Type, *request.Configuration[0].Token) + + data, err := xml.Marshal(request) + require.NoError(t, err) + + assert.Equal(t, expected, string(data)) + +} + +func TestMarshalRemoveConfigurationRequest(t *testing.T) { + analyticsType := xsd.String("Analytics") + analyticsToken := xsd.String("AnalyticsToken") + request := RemoveConfiguration{ + ProfileToken: "profile_1", + Configuration: []Configuration{ + { + Type: &analyticsType, + Token: &analyticsToken, + }, + }, + } + expected := fmt.Sprintf("%s%s%s", + request.ProfileToken, *request.Configuration[0].Type, *request.Configuration[0].Token) + + data, err := xml.Marshal(request) + require.NoError(t, err) + + assert.Equal(t, expected, string(data)) +} diff --git a/names.go b/names.go new file mode 100644 index 0000000..e3cbaea --- /dev/null +++ b/names.go @@ -0,0 +1,297 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package onvif + +// Onvif WebService +const ( + AnalyticsWebService = "Analytics" + DeviceWebService = "Device" + EventWebService = "Event" + ImagingWebService = "Imaging" + MediaWebService = "Media" + Media2WebService = "Media2" + PTZWebService = "PTZ" + RecordingWebService = "Recording" +) + +// WebService - Analytics +const ( + CreateAnalyticsModules = "CreateAnalyticsModules" + CreateRules = "CreateRules" + DeleteAnalyticsModules = "DeleteAnalyticsModules" + DeleteRules = "DeleteRules" + GetAnalyticsModuleOptions = "GetAnalyticsModuleOptions" + GetAnalyticsModules = "GetAnalyticsModules" + GetRuleOptions = "GetRuleOptions" + GetRules = "GetRules" + GetSupportedAnalyticsModules = "GetSupportedAnalyticsModules" + GetSupportedRules = "GetSupportedRules" + ModifyAnalyticsModules = "ModifyAnalyticsModules" + ModifyRules = "ModifyRules" +) + +// WebService - Device +const ( + AddIPAddressFilter = "AddIPAddressFilter" + AddScopes = "AddScopes" + CreateCertificate = "CreateCertificate" + CreateDot1XConfiguration = "CreateDot1XConfiguration" + CreateStorageConfiguration = "CreateStorageConfiguration" + CreateUsers = "CreateUsers" + DeleteCertificates = "DeleteCertificates" + DeleteDot1XConfiguration = "DeleteDot1XConfiguration" + DeleteGeoLocation = "DeleteGeoLocation" + DeleteStorageConfiguration = "DeleteStorageConfiguration" + DeleteUsers = "DeleteUsers" + GetAccessPolicy = "GetAccessPolicy" + GetCACertificates = "GetCACertificates" + GetCapabilities = "GetCapabilities" + GetCertificateInformation = "GetCertificateInformation" + GetCertificates = "GetCertificates" + GetCertificatesStatus = "GetCertificatesStatus" + GetClientCertificateMode = "GetClientCertificateMode" + GetDNS = "GetDNS" + GetDPAddresses = "GetDPAddresses" + GetDeviceInformation = "GetDeviceInformation" + GetDiscoveryMode = "GetDiscoveryMode" + GetDot11Capabilities = "GetDot11Capabilities" + GetDot11Status = "GetDot11Status" + GetDot1XConfiguration = "GetDot1XConfiguration" + GetDot1XConfigurations = "GetDot1XConfigurations" + GetDynamicDNS = "GetDynamicDNS" + GetEndpointReference = "GetEndpointReference" + GetGeoLocation = "GetGeoLocation" + GetHostname = "GetHostname" + GetIPAddressFilter = "GetIPAddressFilter" + GetNTP = "GetNTP" + GetNetworkDefaultGateway = "GetNetworkDefaultGateway" + GetNetworkInterfaces = "GetNetworkInterfaces" + GetNetworkProtocols = "GetNetworkProtocols" + GetPkcs10Request = "GetPkcs10Request" + GetRelayOutputs = "GetRelayOutputs" + GetRemoteDiscoveryMode = "GetRemoteDiscoveryMode" + GetRemoteUser = "GetRemoteUser" + GetScopes = "GetScopes" + GetServiceCapabilities = "GetServiceCapabilities" + GetServices = "GetServices" + GetStorageConfiguration = "GetStorageConfiguration" + GetStorageConfigurations = "GetStorageConfigurations" + GetSystemBackup = "GetSystemBackup" + GetSystemDateAndTime = "GetSystemDateAndTime" + GetSystemLog = "GetSystemLog" + GetSystemSupportInformation = "GetSystemSupportInformation" + GetSystemUris = "GetSystemUris" + GetUsers = "GetUsers" + GetWsdlUrl = "GetWsdlUrl" + GetZeroConfiguration = "GetZeroConfiguration" + LoadCACertificates = "LoadCACertificates" + LoadCertificateWithPrivateKey = "LoadCertificateWithPrivateKey" + LoadCertificates = "LoadCertificates" + RemoveIPAddressFilter = "RemoveIPAddressFilter" + RemoveScopes = "RemoveScopes" + RestoreSystem = "RestoreSystem" + ScanAvailableDot11Networks = "ScanAvailableDot11Networks" + SendAuxiliaryCommand = "SendAuxiliaryCommand" + SetAccessPolicy = "SetAccessPolicy" + SetCertificatesStatus = "SetCertificatesStatus" + SetClientCertificateMode = "SetClientCertificateMode" + SetDNS = "SetDNS" + SetDPAddresses = "SetDPAddresses" + SetDiscoveryMode = "SetDiscoveryMode" + SetDot1XConfiguration = "SetDot1XConfiguration" + SetDynamicDNS = "SetDynamicDNS" + SetGeoLocation = "SetGeoLocation" + SetHostname = "SetHostname" + SetHostnameFromDHCP = "SetHostnameFromDHCP" + SetIPAddressFilter = "SetIPAddressFilter" + SetNTP = "SetNTP" + SetNetworkDefaultGateway = "SetNetworkDefaultGateway" + SetNetworkInterfaces = "SetNetworkInterfaces" + SetNetworkProtocols = "SetNetworkProtocols" + SetRelayOutputSettings = "SetRelayOutputSettings" + SetRelayOutputState = "SetRelayOutputState" + SetRemoteDiscoveryMode = "SetRemoteDiscoveryMode" + SetRemoteUser = "SetRemoteUser" + SetScopes = "SetScopes" + SetStorageConfiguration = "SetStorageConfiguration" + SetSystemDateAndTime = "SetSystemDateAndTime" + SetSystemFactoryDefault = "SetSystemFactoryDefault" + SetUser = "SetUser" + SetZeroConfiguration = "SetZeroConfiguration" + StartFirmwareUpgrade = "StartFirmwareUpgrade" + StartSystemRestore = "StartSystemRestore" + SystemReboot = "SystemReboot" + UpgradeSystemFirmware = "UpgradeSystemFirmware" +) + +// WebService - Event +const ( + CreatePullPointSubscription = "CreatePullPointSubscription" + GetEventProperties = "GetEventProperties" + PullMessages = "PullMessages" + Renew = "Renew" + Seek = "Seek" + SetSynchronizationPoint = "SetSynchronizationPoint" + Subscribe = "Subscribe" + SubscriptionReference = "SubscriptionReference" + Unsubscribe = "Unsubscribe" +) + +// WebService - Imaging +const ( + GetCurrentPreset = "GetCurrentPreset" + GetImagingSettings = "GetImagingSettings" + GetMoveOptions = "GetMoveOptions" + GetOptions = "GetOptions" + GetPresets = "GetPresets" + GetStatus = "GetStatus" + Move = "Move" + SetCurrentPreset = "SetCurrentPreset" + SetImagingSettings = "SetImagingSettings" + Stop = "Stop" +) + +// WebService - Media +const ( + AddAudioDecoderConfiguration = "AddAudioDecoderConfiguration" + AddAudioEncoderConfiguration = "AddAudioEncoderConfiguration" + AddAudioOutputConfiguration = "AddAudioOutputConfiguration" + AddAudioSourceConfiguration = "AddAudioSourceConfiguration" + AddMetadataConfiguration = "AddMetadataConfiguration" + AddPTZConfiguration = "AddPTZConfiguration" + AddVideoAnalyticsConfiguration = "AddVideoAnalyticsConfiguration" + AddVideoEncoderConfiguration = "AddVideoEncoderConfiguration" + AddVideoSourceConfiguration = "AddVideoSourceConfiguration" + CreateOSD = "CreateOSD" + CreateProfile = "CreateProfile" + DeleteOSD = "DeleteOSD" + DeleteProfile = "DeleteProfile" + GetAudioDecoderConfiguration = "GetAudioDecoderConfiguration" + GetAudioDecoderConfigurationOptions = "GetAudioDecoderConfigurationOptions" + GetAudioDecoderConfigurations = "GetAudioDecoderConfigurations" + GetAudioEncoderConfiguration = "GetAudioEncoderConfiguration" + GetAudioEncoderConfigurationOptions = "GetAudioEncoderConfigurationOptions" + GetAudioEncoderConfigurations = "GetAudioEncoderConfigurations" + GetAudioOutputConfiguration = "GetAudioOutputConfiguration" + GetAudioOutputConfigurationOptions = "GetAudioOutputConfigurationOptions" + GetAudioOutputConfigurations = "GetAudioOutputConfigurations" + GetAudioOutputs = "GetAudioOutputs" + GetAudioSourceConfiguration = "GetAudioSourceConfiguration" + GetAudioSourceConfigurationOptions = "GetAudioSourceConfigurationOptions" + GetAudioSourceConfigurations = "GetAudioSourceConfigurations" + GetAudioSources = "GetAudioSources" + GetCompatibleAudioDecoderConfigurations = "GetCompatibleAudioDecoderConfigurations" + GetCompatibleAudioEncoderConfigurations = "GetCompatibleAudioEncoderConfigurations" + GetCompatibleAudioOutputConfigurations = "GetCompatibleAudioOutputConfigurations" + GetCompatibleAudioSourceConfigurations = "GetCompatibleAudioSourceConfigurations" + GetCompatibleMetadataConfigurations = "GetCompatibleMetadataConfigurations" + GetCompatibleVideoAnalyticsConfigurations = "GetCompatibleVideoAnalyticsConfigurations" + GetCompatibleVideoEncoderConfigurations = "GetCompatibleVideoEncoderConfigurations" + GetCompatibleVideoSourceConfigurations = "GetCompatibleVideoSourceConfigurations" + GetGuaranteedNumberOfVideoEncoderInstances = "GetGuaranteedNumberOfVideoEncoderInstances" + GetMetadataConfiguration = "GetMetadataConfiguration" + GetMetadataConfigurationOptions = "GetMetadataConfigurationOptions" + GetMetadataConfigurations = "GetMetadataConfigurations" + GetOSD = "GetOSD" + GetOSDOptions = "GetOSDOptions" + GetOSDs = "GetOSDs" + GetProfile = "GetProfile" + GetProfiles = "GetProfiles" + GetSnapshotUri = "GetSnapshotUri" + GetStreamUri = "GetStreamUri" + GetVideoAnalyticsConfiguration = "GetVideoAnalyticsConfiguration" + GetVideoAnalyticsConfigurations = "GetVideoAnalyticsConfigurations" + GetVideoEncoderConfiguration = "GetVideoEncoderConfiguration" + GetVideoEncoderConfigurationOptions = "GetVideoEncoderConfigurationOptions" + GetVideoEncoderConfigurations = "GetVideoEncoderConfigurations" + GetVideoSourceConfiguration = "GetVideoSourceConfiguration" + GetVideoSourceConfigurationOptions = "GetVideoSourceConfigurationOptions" + GetVideoSourceConfigurations = "GetVideoSourceConfigurations" + GetVideoSourceModes = "GetVideoSourceModes" + GetVideoSources = "GetVideoSources" + RemoveAudioDecoderConfiguration = "RemoveAudioDecoderConfiguration" + RemoveAudioEncoderConfiguration = "RemoveAudioEncoderConfiguration" + RemoveAudioOutputConfiguration = "RemoveAudioOutputConfiguration" + RemoveAudioSourceConfiguration = "RemoveAudioSourceConfiguration" + RemoveMetadataConfiguration = "RemoveMetadataConfiguration" + RemovePTZConfiguration = "RemovePTZConfiguration" + RemoveVideoAnalyticsConfiguration = "RemoveVideoAnalyticsConfiguration" + RemoveVideoEncoderConfiguration = "RemoveVideoEncoderConfiguration" + RemoveVideoSourceConfiguration = "RemoveVideoSourceConfiguration" + SetAudioDecoderConfiguration = "SetAudioDecoderConfiguration" + SetAudioEncoderConfiguration = "SetAudioEncoderConfiguration" + SetAudioOutputConfiguration = "SetAudioOutputConfiguration" + SetAudioSourceConfiguration = "SetAudioSourceConfiguration" + SetMetadataConfiguration = "SetMetadataConfiguration" + SetOSD = "SetOSD" + SetVideoAnalyticsConfiguration = "SetVideoAnalyticsConfiguration" + SetVideoEncoderConfiguration = "SetVideoEncoderConfiguration" + SetVideoSourceConfiguration = "SetVideoSourceConfiguration" + SetVideoSourceMode = "SetVideoSourceMode" + StartMulticastStreaming = "StartMulticastStreaming" + StopMulticastStreaming = "StopMulticastStreaming" +) + +// WebService - Media2 +const ( + AddConfiguration = "AddConfiguration" + GetAnalyticsConfigurations = "GetAnalyticsConfigurations" + RemoveConfiguration = "RemoveConfiguration" +) + +// WebService - PTZ +const ( + AbsoluteMove = "AbsoluteMove" + ContinuousMove = "ContinuousMove" + CreatePresetTour = "CreatePresetTour" + GeoMove = "GeoMove" + GetCompatibleConfigurations = "GetCompatibleConfigurations" + GetConfiguration = "GetConfiguration" + GetConfigurationOptions = "GetConfigurationOptions" + GetConfigurations = "GetConfigurations" + GetNode = "GetNode" + GetNodes = "GetNodes" + GetPresetTour = "GetPresetTour" + GetPresetTourOptions = "GetPresetTourOptions" + GetPresetTours = "GetPresetTours" + GotoHomePosition = "GotoHomePosition" + GotoPreset = "GotoPreset" + ModifyPresetTour = "ModifyPresetTour" + OperatePresetTour = "OperatePresetTour" + RelativeMove = "RelativeMove" + RemovePreset = "RemovePreset" + RemovePresetTour = "RemovePresetTour" + SetConfiguration = "SetConfiguration" + SetHomePosition = "SetHomePosition" + SetPreset = "SetPreset" +) + +// WebService - Recording +const ( + CreateRecording = "CreateRecording" + CreateRecordingJob = "CreateRecordingJob" + CreateTrack = "CreateTrack" + DeleteRecording = "DeleteRecording" + DeleteRecordingJob = "DeleteRecordingJob" + DeleteTrack = "DeleteTrack" + ExportRecordedData = "ExportRecordedData" + GetExportRecordedDataState = "GetExportRecordedDataState" + GetRecordingConfiguration = "GetRecordingConfiguration" + GetRecordingJobConfiguration = "GetRecordingJobConfiguration" + GetRecordingJobState = "GetRecordingJobState" + GetRecordingJobs = "GetRecordingJobs" + GetRecordingOptions = "GetRecordingOptions" + GetRecordings = "GetRecordings" + GetTrackConfiguration = "GetTrackConfiguration" + SetRecordingConfiguration = "SetRecordingConfiguration" + SetRecordingJobConfiguration = "SetRecordingJobConfiguration" + SetRecordingJobMode = "SetRecordingJobMode" + SetTrackConfiguration = "SetTrackConfiguration" + StopExportRecordedData = "StopExportRecordedData" +) diff --git a/networking/networking.go b/networking/networking.go index f86f4e0..7e1aef2 100644 --- a/networking/networking.go +++ b/networking/networking.go @@ -3,15 +3,13 @@ package networking import ( "bytes" "net/http" - - "github.com/juju/errors" ) // SendSoap send soap message func SendSoap(httpClient *http.Client, endpoint, message string) (*http.Response, error) { resp, err := httpClient.Post(endpoint, "application/soap+xml; charset=utf-8", bytes.NewBufferString(message)) if err != nil { - return resp, errors.Annotate(err, "Post") + return resp, err } return resp, nil diff --git a/ptz/function.go b/ptz/function.go new file mode 100644 index 0000000..0143d1b --- /dev/null +++ b/ptz/function.go @@ -0,0 +1,261 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package ptz + +type AbsoluteMoveFunction struct{} + +func (_ *AbsoluteMoveFunction) Request() interface{} { + return &AbsoluteMove{} +} +func (_ *AbsoluteMoveFunction) Response() interface{} { + return &AbsoluteMoveResponse{} +} + +type ContinuousMoveFunction struct{} + +func (_ *ContinuousMoveFunction) Request() interface{} { + return &ContinuousMove{} +} +func (_ *ContinuousMoveFunction) Response() interface{} { + return &ContinuousMoveResponse{} +} + +type CreatePresetTourFunction struct{} + +func (_ *CreatePresetTourFunction) Request() interface{} { + return &CreatePresetTour{} +} +func (_ *CreatePresetTourFunction) Response() interface{} { + return &CreatePresetTourResponse{} +} + +type GeoMoveFunction struct{} + +func (_ *GeoMoveFunction) Request() interface{} { + return &GeoMove{} +} +func (_ *GeoMoveFunction) Response() interface{} { + return &GeoMoveResponse{} +} + +type GetCompatibleConfigurationsFunction struct{} + +func (_ *GetCompatibleConfigurationsFunction) Request() interface{} { + return &GetCompatibleConfigurations{} +} +func (_ *GetCompatibleConfigurationsFunction) Response() interface{} { + return &GetCompatibleConfigurationsResponse{} +} + +type GetConfigurationFunction struct{} + +func (_ *GetConfigurationFunction) Request() interface{} { + return &GetConfiguration{} +} +func (_ *GetConfigurationFunction) Response() interface{} { + return &GetConfigurationResponse{} +} + +type GetConfigurationOptionsFunction struct{} + +func (_ *GetConfigurationOptionsFunction) Request() interface{} { + return &GetConfigurationOptions{} +} +func (_ *GetConfigurationOptionsFunction) Response() interface{} { + return &GetConfigurationOptionsResponse{} +} + +type GetConfigurationsFunction struct{} + +func (_ *GetConfigurationsFunction) Request() interface{} { + return &GetConfigurations{} +} +func (_ *GetConfigurationsFunction) Response() interface{} { + return &GetConfigurationsResponse{} +} + +type GetNodeFunction struct{} + +func (_ *GetNodeFunction) Request() interface{} { + return &GetNode{} +} +func (_ *GetNodeFunction) Response() interface{} { + return &GetNodeResponse{} +} + +type GetNodesFunction struct{} + +func (_ *GetNodesFunction) Request() interface{} { + return &GetNodes{} +} +func (_ *GetNodesFunction) Response() interface{} { + return &GetNodesResponse{} +} + +type GetPresetTourFunction struct{} + +func (_ *GetPresetTourFunction) Request() interface{} { + return &GetPresetTour{} +} +func (_ *GetPresetTourFunction) Response() interface{} { + return &GetPresetTourResponse{} +} + +type GetPresetTourOptionsFunction struct{} + +func (_ *GetPresetTourOptionsFunction) Request() interface{} { + return &GetPresetTourOptions{} +} +func (_ *GetPresetTourOptionsFunction) Response() interface{} { + return &GetPresetTourOptionsResponse{} +} + +type GetPresetToursFunction struct{} + +func (_ *GetPresetToursFunction) Request() interface{} { + return &GetPresetTours{} +} +func (_ *GetPresetToursFunction) Response() interface{} { + return &GetPresetToursResponse{} +} + +type GetPresetsFunction struct{} + +func (_ *GetPresetsFunction) Request() interface{} { + return &GetPresets{} +} +func (_ *GetPresetsFunction) Response() interface{} { + return &GetPresetsResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type GetStatusFunction struct{} + +func (_ *GetStatusFunction) Request() interface{} { + return &GetStatus{} +} +func (_ *GetStatusFunction) Response() interface{} { + return &GetStatusResponse{} +} + +type GotoHomePositionFunction struct{} + +func (_ *GotoHomePositionFunction) Request() interface{} { + return &GotoHomePosition{} +} +func (_ *GotoHomePositionFunction) Response() interface{} { + return &GotoHomePositionResponse{} +} + +type GotoPresetFunction struct{} + +func (_ *GotoPresetFunction) Request() interface{} { + return &GotoPreset{} +} +func (_ *GotoPresetFunction) Response() interface{} { + return &GotoPresetResponse{} +} + +type ModifyPresetTourFunction struct{} + +func (_ *ModifyPresetTourFunction) Request() interface{} { + return &ModifyPresetTour{} +} +func (_ *ModifyPresetTourFunction) Response() interface{} { + return &ModifyPresetTourResponse{} +} + +type OperatePresetTourFunction struct{} + +func (_ *OperatePresetTourFunction) Request() interface{} { + return &OperatePresetTour{} +} +func (_ *OperatePresetTourFunction) Response() interface{} { + return &OperatePresetTourResponse{} +} + +type RelativeMoveFunction struct{} + +func (_ *RelativeMoveFunction) Request() interface{} { + return &RelativeMove{} +} +func (_ *RelativeMoveFunction) Response() interface{} { + return &RelativeMoveResponse{} +} + +type RemovePresetFunction struct{} + +func (_ *RemovePresetFunction) Request() interface{} { + return &RemovePreset{} +} +func (_ *RemovePresetFunction) Response() interface{} { + return &RemovePresetResponse{} +} + +type RemovePresetTourFunction struct{} + +func (_ *RemovePresetTourFunction) Request() interface{} { + return &RemovePresetTour{} +} +func (_ *RemovePresetTourFunction) Response() interface{} { + return &RemovePresetTourResponse{} +} + +type SendAuxiliaryCommandFunction struct{} + +func (_ *SendAuxiliaryCommandFunction) Request() interface{} { + return &SendAuxiliaryCommand{} +} +func (_ *SendAuxiliaryCommandFunction) Response() interface{} { + return &SendAuxiliaryCommandResponse{} +} + +type SetConfigurationFunction struct{} + +func (_ *SetConfigurationFunction) Request() interface{} { + return &SetConfiguration{} +} +func (_ *SetConfigurationFunction) Response() interface{} { + return &SetConfigurationResponse{} +} + +type SetHomePositionFunction struct{} + +func (_ *SetHomePositionFunction) Request() interface{} { + return &SetHomePosition{} +} +func (_ *SetHomePositionFunction) Response() interface{} { + return &SetHomePositionResponse{} +} + +type SetPresetFunction struct{} + +func (_ *SetPresetFunction) Request() interface{} { + return &SetPreset{} +} +func (_ *SetPresetFunction) Response() interface{} { + return &SetPresetResponse{} +} + +type StopFunction struct{} + +func (_ *StopFunction) Request() interface{} { + return &Stop{} +} +func (_ *StopFunction) Response() interface{} { + return &StopResponse{} +} diff --git a/ptz/types.go b/ptz/types.go index f21dd57..e07c3fc 100644 --- a/ptz/types.go +++ b/ptz/types.go @@ -1,5 +1,7 @@ package ptz +//go:generate python3 ../python/gen_commands.py + import ( "github.com/kerberos-io/onvif/xsd" "github.com/kerberos-io/onvif/xsd/onvif" @@ -28,7 +30,7 @@ type GetNodes struct { } type GetNodesResponse struct { - PTZNode onvif.PTZNode + PTZNode []onvif.PTZNode } type GetNode struct { @@ -41,8 +43,8 @@ type GetNodeResponse struct { } type GetConfiguration struct { - XMLName string `xml:"tptz:GetConfiguration"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` + XMLName string `xml:"tptz:GetConfiguration"` + PTZConfigurationToken onvif.ReferenceToken `xml:"tptz:PTZConfigurationToken"` } type GetConfigurationResponse struct { @@ -54,7 +56,7 @@ type GetConfigurations struct { } type GetConfigurationsResponse struct { - PTZConfiguration onvif.PTZConfiguration + PTZConfiguration []onvif.PTZConfiguration } type SetConfiguration struct { @@ -67,8 +69,8 @@ type SetConfigurationResponse struct { } type GetConfigurationOptions struct { - XMLName string `xml:"tptz:GetConfigurationOptions"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` + XMLName string `xml:"tptz:GetConfigurationOptions"` + ConfigurationToken onvif.ReferenceToken `xml:"tptz:ConfigurationToken"` } type GetConfigurationOptionsResponse struct { @@ -95,10 +97,10 @@ type GetPresetsResponse struct { } type SetPreset struct { - XMLName string `xml:"tptz:SetPreset"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` - PresetName xsd.String `xml:"tptz:PresetName"` - PresetToken onvif.ReferenceToken `xml:"tptz:PresetToken,omitempty"` + XMLName string `xml:"tptz:SetPreset"` + ProfileToken *onvif.ReferenceToken `xml:"tptz:ProfileToken,omitempty"` + PresetName *xsd.String `xml:"tptz:PresetName,omitempty"` + PresetToken *onvif.ReferenceToken `xml:"tptz:PresetToken,omitempty"` } type SetPresetResponse struct { @@ -115,19 +117,19 @@ type RemovePresetResponse struct { } type GotoPreset struct { - XMLName string `xml:"tptz:GotoPreset"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` - PresetToken onvif.ReferenceToken `xml:"tptz:PresetToken"` - Speed onvif.PTZSpeed `xml:"tptz:Speed"` + XMLName string `xml:"tptz:GotoPreset,omitempty"` + ProfileToken *onvif.ReferenceToken `xml:"tptz:ProfileToken,omitempty"` + PresetToken *onvif.ReferenceToken `xml:"tptz:PresetToken,omitempty"` + Speed *onvif.PTZSpeed `xml:"tptz:Speed,omitempty"` } type GotoPresetResponse struct { } type GotoHomePosition struct { - XMLName string `xml:"tptz:GotoHomePosition"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` - Speed onvif.PTZSpeed `xml:"tptz:Speed"` + XMLName string `xml:"tptz:GotoHomePosition"` + ProfileToken *onvif.ReferenceToken `xml:"tptz:ProfileToken,omitempty"` + Speed *onvif.PTZSpeed `xml:"tptz:Speed,omitempty"` } type GotoHomePositionResponse struct { @@ -142,20 +144,20 @@ type SetHomePositionResponse struct { } type ContinuousMove struct { - XMLName string `xml:"tptz:ContinuousMove"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` - Velocity onvif.PTZSpeed `xml:"tptz:Velocity"` - //Timeout xsd.Duration `xml:"tptz:Timeout"` + XMLName string `xml:"tptz:ContinuousMove"` + ProfileToken *onvif.ReferenceToken `xml:"tptz:ProfileToken,omitempty"` + Velocity onvif.PTZSpeed `xml:"tptz:Velocity,omitempty"` + Timeout *xsd.Duration `xml:"tptz:Timeout,omitempty"` } type ContinuousMoveResponse struct { } type RelativeMove struct { - XMLName string `xml:"tptz:RelativeMove,omitempty"` - ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken,omitempty"` - Translation onvif.PTZVector `xml:"tptz:Translation,omitempty"` - Speed onvif.PTZSpeed `xml:"tptz:Speed,omitempty"` + XMLName string `xml:"tptz:RelativeMove"` + ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` + Translation Vector `json:",omitempty" xml:"tptz:Translation,omitempty"` + Speed Speed `json:",omitempty" xml:"tptz:Speed,omitempty"` } type RelativeMoveResponse struct { @@ -177,6 +179,16 @@ type AbsoluteMove struct { Speed onvif.PTZSpeed `xml:"tptz:Speed"` } +type Vector struct { + PanTilt *onvif.Vector2D `json:",omitempty" xml:"onvif:PanTilt,omitempty"` + Zoom *onvif.Vector1D `json:",omitempty" xml:"onvif:Zoom,omitempty"` +} + +type Speed struct { + PanTilt *onvif.Vector2D `json:",omitempty" xml:"onvif:PanTilt,omitempty"` + Zoom *onvif.Vector1D `json:",omitempty" xml:"onvif:Zoom,omitempty"` +} + type AbsoluteMoveResponse struct { } diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 0000000..28929c8 --- /dev/null +++ b/python/Makefile @@ -0,0 +1,11 @@ +# Copyright (C) 2022 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +.PHONY: gen + +gen: + python3 gen_commands.py + which go + go fmt ../... + diff --git a/python/gen_commands.py b/python/gen_commands.py new file mode 100755 index 0000000..e0be690 --- /dev/null +++ b/python/gen_commands.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 + +import dataclasses +import pathlib +import re +import subprocess + + +# license header to use for generated go files +HEADER = '''\ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +''' + +BASE_MODULE_NAME = 'github.com/kerberos-io/onvif' + +SERVICE_NAMES = [ + 'analytics', + 'device', + 'event', + 'imaging', + 'media', + 'media2', + 'ptz', + 'recording', +] + + +@dataclasses.dataclass +class FunctionsGenerator: + service_name: str + input_file: pathlib.Path + output_file: pathlib.Path + commands = [] + + @staticmethod + def make_function(cmd): + return '''\ +type %sFunction struct{} + +func (_ *%sFunction) Request() interface{} { +\treturn &%s{} +} +func (_ *%sFunction) Response() interface{} { +\treturn &%sResponse{} +} + +''' % (cmd, cmd, cmd, cmd, cmd) + + def run(self): + self.commands = [] + with open(self.input_file) as f: + with open(self.output_file, 'w') as w: + w.write(HEADER) + w.write(f'package {self.service_name}\n\n') + + while line := f.readline(): + if m := re.search(r'type ([^ ]+)Response struct', line): + cmd = m.group(1) + if cmd.endswith('Fault'): + continue # skip Fault responses + self.commands.append(cmd) + + self.commands = sorted(self.commands) + for cmd in self.commands: + print(f'{self.service_name}.{cmd}') + w.write(FunctionsGenerator.make_function(cmd)) + + def write_func_map(self, w): + w.write('var %sFunctionMap = map[string]Function{\n' % self.proper_name) + for cmd in self.commands: + w.write('\t%s: &%s.%sFunction{},\n' % (cmd, self.service_name, cmd)) + w.write('}\n\n') + + @property + def proper_name(self): + return (self.service_name[0].upper() + self.service_name[1:]).replace('Ptz', 'PTZ') + + +@dataclasses.dataclass +class MainGenerator: + root_dir: pathlib.Path + generators = [] + unique_cmds = set() + + def _generate_mappings(self): + with open(self.root_dir.joinpath('mappings.go'), 'w') as w: + w.write(HEADER) + w.write('package onvif\n\n') + w.write('import (\n') + for service_name in SERVICE_NAMES: + w.write(f'\t"{BASE_MODULE_NAME}/{service_name}"\n') + w.write(')\n\n') + + for generator in self.generators: + generator.write_func_map(w) + + def _generate_names(self): + with open(self.root_dir.joinpath('names.go'), 'w') as w: + w.write(HEADER) + w.write('package onvif\n\n') + w.write('// Onvif WebService\n') + w.write('const (\n') + for gen in self.generators: + w.write(f'\t{gen.proper_name}WebService = "{gen.proper_name}"\n') + w.write(')\n\n') + + for gen in self.generators: + w.write(f'// WebService - {gen.proper_name}\n') + w.write('const (\n') + for cmd in gen.commands: + if cmd not in self.unique_cmds: + w.write(f'\t{cmd} = "{cmd}"\n') + self.unique_cmds.add(cmd) + w.write(')\n\n') + + def run(self): + for service_name in SERVICE_NAMES: + gen = FunctionsGenerator(service_name, + self.root_dir.joinpath(f'{service_name}/types.go'), + self.root_dir.joinpath(f'{service_name}/function.go')) + gen.run() + self.generators.append(gen) + + self._generate_mappings() + self._generate_names() + + +def main(): + root_dir = pathlib.Path(__file__).parent.parent.resolve() + main_gen = MainGenerator(root_dir) + main_gen.run() + + print('\n\nFormatting generated files...') + try: + subprocess.check_call(['go', 'fmt', str(root_dir) + "/..."]) + except OSError as e: + print(f"\n\033[31;1mError occurred while trying to format generated files: {e}\033[0m") + print("\033[33;1mPlease run 'go fmt ./...' manually to cleanup generated code formatting!\033[0m") + + +if __name__ == '__main__': + main() diff --git a/recording/function.go b/recording/function.go new file mode 100644 index 0000000..d29c93e --- /dev/null +++ b/recording/function.go @@ -0,0 +1,198 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +// Copyright (C) 2022 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen_commands.py DO NOT EDIT. + +package recording + +type CreateRecordingFunction struct{} + +func (_ *CreateRecordingFunction) Request() interface{} { + return &CreateRecording{} +} +func (_ *CreateRecordingFunction) Response() interface{} { + return &CreateRecordingResponse{} +} + +type CreateRecordingJobFunction struct{} + +func (_ *CreateRecordingJobFunction) Request() interface{} { + return &CreateRecordingJob{} +} +func (_ *CreateRecordingJobFunction) Response() interface{} { + return &CreateRecordingJobResponse{} +} + +type CreateTrackFunction struct{} + +func (_ *CreateTrackFunction) Request() interface{} { + return &CreateTrack{} +} +func (_ *CreateTrackFunction) Response() interface{} { + return &CreateTrackResponse{} +} + +type DeleteRecordingFunction struct{} + +func (_ *DeleteRecordingFunction) Request() interface{} { + return &DeleteRecording{} +} +func (_ *DeleteRecordingFunction) Response() interface{} { + return &DeleteRecordingResponse{} +} + +type DeleteRecordingJobFunction struct{} + +func (_ *DeleteRecordingJobFunction) Request() interface{} { + return &DeleteRecordingJob{} +} +func (_ *DeleteRecordingJobFunction) Response() interface{} { + return &DeleteRecordingJobResponse{} +} + +type DeleteTrackFunction struct{} + +func (_ *DeleteTrackFunction) Request() interface{} { + return &DeleteTrack{} +} +func (_ *DeleteTrackFunction) Response() interface{} { + return &DeleteTrackResponse{} +} + +type ExportRecordedDataFunction struct{} + +func (_ *ExportRecordedDataFunction) Request() interface{} { + return &ExportRecordedData{} +} +func (_ *ExportRecordedDataFunction) Response() interface{} { + return &ExportRecordedDataResponse{} +} + +type GetExportRecordedDataStateFunction struct{} + +func (_ *GetExportRecordedDataStateFunction) Request() interface{} { + return &GetExportRecordedDataState{} +} +func (_ *GetExportRecordedDataStateFunction) Response() interface{} { + return &GetExportRecordedDataStateResponse{} +} + +type GetRecordingConfigurationFunction struct{} + +func (_ *GetRecordingConfigurationFunction) Request() interface{} { + return &GetRecordingConfiguration{} +} +func (_ *GetRecordingConfigurationFunction) Response() interface{} { + return &GetRecordingConfigurationResponse{} +} + +type GetRecordingJobConfigurationFunction struct{} + +func (_ *GetRecordingJobConfigurationFunction) Request() interface{} { + return &GetRecordingJobConfiguration{} +} +func (_ *GetRecordingJobConfigurationFunction) Response() interface{} { + return &GetRecordingJobConfigurationResponse{} +} + +type GetRecordingJobStateFunction struct{} + +func (_ *GetRecordingJobStateFunction) Request() interface{} { + return &GetRecordingJobState{} +} +func (_ *GetRecordingJobStateFunction) Response() interface{} { + return &GetRecordingJobStateResponse{} +} + +type GetRecordingJobsFunction struct{} + +func (_ *GetRecordingJobsFunction) Request() interface{} { + return &GetRecordingJobs{} +} +func (_ *GetRecordingJobsFunction) Response() interface{} { + return &GetRecordingJobsResponse{} +} + +type GetRecordingOptionsFunction struct{} + +func (_ *GetRecordingOptionsFunction) Request() interface{} { + return &GetRecordingOptions{} +} +func (_ *GetRecordingOptionsFunction) Response() interface{} { + return &GetRecordingOptionsResponse{} +} + +type GetRecordingsFunction struct{} + +func (_ *GetRecordingsFunction) Request() interface{} { + return &GetRecordings{} +} +func (_ *GetRecordingsFunction) Response() interface{} { + return &GetRecordingsResponse{} +} + +type GetServiceCapabilitiesFunction struct{} + +func (_ *GetServiceCapabilitiesFunction) Request() interface{} { + return &GetServiceCapabilities{} +} +func (_ *GetServiceCapabilitiesFunction) Response() interface{} { + return &GetServiceCapabilitiesResponse{} +} + +type GetTrackConfigurationFunction struct{} + +func (_ *GetTrackConfigurationFunction) Request() interface{} { + return &GetTrackConfiguration{} +} +func (_ *GetTrackConfigurationFunction) Response() interface{} { + return &GetTrackConfigurationResponse{} +} + +type SetRecordingConfigurationFunction struct{} + +func (_ *SetRecordingConfigurationFunction) Request() interface{} { + return &SetRecordingConfiguration{} +} +func (_ *SetRecordingConfigurationFunction) Response() interface{} { + return &SetRecordingConfigurationResponse{} +} + +type SetRecordingJobConfigurationFunction struct{} + +func (_ *SetRecordingJobConfigurationFunction) Request() interface{} { + return &SetRecordingJobConfiguration{} +} +func (_ *SetRecordingJobConfigurationFunction) Response() interface{} { + return &SetRecordingJobConfigurationResponse{} +} + +type SetRecordingJobModeFunction struct{} + +func (_ *SetRecordingJobModeFunction) Request() interface{} { + return &SetRecordingJobMode{} +} +func (_ *SetRecordingJobModeFunction) Response() interface{} { + return &SetRecordingJobModeResponse{} +} + +type SetTrackConfigurationFunction struct{} + +func (_ *SetTrackConfigurationFunction) Request() interface{} { + return &SetTrackConfiguration{} +} +func (_ *SetTrackConfigurationFunction) Response() interface{} { + return &SetTrackConfigurationResponse{} +} + +type StopExportRecordedDataFunction struct{} + +func (_ *StopExportRecordedDataFunction) Request() interface{} { + return &StopExportRecordedData{} +} +func (_ *StopExportRecordedDataFunction) Response() interface{} { + return &StopExportRecordedDataResponse{} +} diff --git a/recording/types.go b/recording/types.go new file mode 100644 index 0000000..fab6fb0 --- /dev/null +++ b/recording/types.go @@ -0,0 +1,793 @@ +package recording + +import ( + "encoding/xml" + + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" +) + +// TrackType type +type TrackType string + +const ( + // TrackTypeVideo const + TrackTypeVideo TrackType = "Video" + + // TrackTypeAudio const + TrackTypeAudio TrackType = "Audio" + + // TrackTypeMetadata const + TrackTypeMetadata TrackType = "Metadata" + + // Placeholder for future extension. + // TrackTypeExtended const + TrackTypeExtended TrackType = "Extended" +) + +// EncodingTypes type +type EncodingTypes []string + +// GetServiceCapabilities type +type GetServiceCapabilities struct { + XMLName xml.Name `xml:"tt:GetServiceCapabilities"` +} + +// GetServiceCapabilitiesResponse type +type GetServiceCapabilitiesResponse struct { + XMLName xml.Name `xml:"GetServiceCapabilitiesResponse"` + + // The capabilities for the recording service is returned in the Capabilities element. + Capabilities Capabilities `xml:"Capabilities,omitempty"` +} + +// CreateRecording type +type CreateRecording struct { + XMLName xml.Name `xml:"tt:CreateRecording"` + + // Initial configuration for the recording. + RecordingConfiguration RecordingConfiguration `xml:"tt:RecordingConfiguration,omitempty"` +} + +// RecordingConfiguration type +type RecordingConfiguration struct { + + // Information about the source of the recording. + Source RecordingSourceInformation `xml:"tt:Source,omitempty"` + + // Informative description of the source. + Content onvif.Description `xml:"tt:Content,omitempty"` + + // Sspecifies the maximum time that data in any track within the + // recording shall be stored. The device shall delete any data older than the maximum retention + // time. Such data shall not be accessible anymore. If the MaximumRetentionPeriod is set to 0, + // the device shall not limit the retention time of stored data, except by resource constraints. + // Whatever the value of MaximumRetentionTime, the device may automatically delete + // recordings to free up storage space for new recordings. + MaximumRetentionTime xsd.Duration `xml:"tt:MaximumRetentionTime,omitempty"` +} + +// RecordingSourceInformation type +type RecordingSourceInformation struct { + + // + // Identifier for the source chosen by the client that creates the structure. + // This identifier is opaque to the device. Clients may use any type of URI for this field. A device shall support at least 128 characters. + SourceId xsd.AnyURI `xml:"tt:SourceId,omitempty"` + + // Informative user readable name of the source, e.g. "Camera23". A device shall support at least 20 characters. + Name xsd.Name `xml:"tt:Name,omitempty"` + + // Informative description of the physical location of the source, e.g. the coordinates on a map. + Location onvif.Description `xml:"tt:Location,omitempty"` + + // Informative description of the source. + Description onvif.Description `xml:"tt:Description,omitempty"` + + // URI provided by the service supplying data to be recorded. A device shall support at least 128 characters. + Address xsd.AnyURI `xml:"tt:Address,omitempty"` +} + +// CreateRecordingResponse type +type CreateRecordingResponse struct { + XMLName xml.Name `xml:"CreateRecordingResponse"` + + // The reference to the created recording. + RecordingToken RecordingReference `xml:"RecordingToken,omitempty"` +} + +// DeleteRecording type +type DeleteRecording struct { + XMLName xml.Name `xml:"tt:DeleteRecording"` + + // The reference of the recording to be deleted. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` +} + +// DeleteRecordingResponse type +type DeleteRecordingResponse struct { + XMLName xml.Name `xml:"DeleteRecordingResponse"` +} + +// GetRecordings type +type GetRecordings struct { + XMLName xml.Name `xml:"tt:GetRecordings"` +} + +// GetRecordingsResponse type +type GetRecordingsResponse struct { + XMLName xml.Name `xml:"GetRecordingsResponse"` + + // List of recording items. + RecordingItem []GetRecordingsResponseItem +} + +// GetRecordingsResponseItem type +type GetRecordingsResponseItem struct { + // Token of the recording. + RecordingToken RecordingReference + + // Configuration of the recording. + Configuration struct { + Source struct { + SourceId xsd.AnyURI + Name xsd.Name + Location xsd.String + Description xsd.String + Address xsd.AnyURI + } + Content xsd.String + MaximumRetentionTime xsd.Duration + } + + // List of tracks. + Tracks GetTracksResponseList +} + +// GetTracksResponseList type +type GetTracksResponseList struct { + // Configuration of a track. + Track []GetTracksResponseItem +} + +// GetTracksResponseItem type +type GetTracksResponseItem struct { + // Token of the track. + TrackToken TrackReference + // Configuration of the track. + Configuration struct { + TrackType TrackType + Description xsd.String + } +} + +// TrackConfiguration type +type TrackConfiguration struct { + + // Type of the track. It shall be equal to the strings “Video”, + // “Audio” or “Metadata”. The track shall only be able to hold data of that type. + TrackType TrackType `xml:"tt:TrackType,omitempty"` + + // Informative description of the track. + Description onvif.Description `xml:"tt:Description,omitempty"` +} + +// SetRecordingConfiguration type +type SetRecordingConfiguration struct { + XMLName xml.Name `xml:"tt:SetRecordingConfiguration"` + + // Token of the recording that shall be changed. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // The new configuration. + RecordingConfiguration RecordingConfiguration `xml:"tt:RecordingConfiguration,omitempty"` +} + +// SetRecordingConfigurationResponse type +type SetRecordingConfigurationResponse struct { + XMLName xml.Name `xml:"SetRecordingConfigurationResponse"` +} + +// GetRecordingConfiguration type +type GetRecordingConfiguration struct { + XMLName xml.Name `xml:"tt:GetRecordingConfiguration"` + + // Token of the configuration to be retrieved. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` +} + +// GetRecordingConfigurationResponse type +type GetRecordingConfigurationResponse struct { + XMLName xml.Name `xml:"GetRecordingConfigurationResponse"` + + // Configuration of the recording. + RecordingConfiguration RecordingConfiguration `xml:"RecordingConfiguration,omitempty"` +} + +// CreateTrack type +type CreateTrack struct { + XMLName xml.Name `xml:"tt:CreateTrack"` + + // Identifies the recording to which a track shall be added. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // The configuration of the new track. + TrackConfiguration TrackConfiguration `xml:"tt:TrackConfiguration,omitempty"` +} + +// CreateTrackResponse type +type CreateTrackResponse struct { + XMLName xml.Name `xml:"CreateTrackResponse"` + + // The TrackToken shall identify the newly created track. The + // TrackToken shall be unique within the recoding to which + // the new track belongs. + TrackToken TrackReference `xml:"TrackToken,omitempty"` +} + +// DeleteTrack type +type DeleteTrack struct { + XMLName xml.Name `xml:"tt:DeleteTrack"` + + // Token of the recording the track belongs to. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // Token of the track to be deleted. + TrackToken TrackReference `xml:"tt:TrackToken,omitempty"` +} + +// DeleteTrackResponse type +type DeleteTrackResponse struct { + XMLName xml.Name `xml:"DeleteTrackResponse"` +} + +// GetTrackConfiguration type +type GetTrackConfiguration struct { + XMLName xml.Name `xml:"tt:GetTrackConfiguration"` + + // Token of the recording the track belongs to. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // Token of the track. + TrackToken TrackReference `xml:"tt:TrackToken,omitempty"` +} + +// GetTrackConfigurationResponse type +type GetTrackConfigurationResponse struct { + XMLName xml.Name `xml:"GetTrackConfigurationResponse"` + + // Configuration of the track. + TrackConfiguration TrackConfiguration `xml:"TrackConfiguration,omitempty"` +} + +// SetTrackConfiguration type +type SetTrackConfiguration struct { + XMLName xml.Name `xml:"tt:SetTrackConfiguration"` + + // Token of the recording the track belongs to. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // Token of the track to be modified. + TrackToken TrackReference `xml:"tt:TrackToken,omitempty"` + + // New configuration for the track. + TrackConfiguration TrackConfiguration `xml:"tt:TrackConfiguration,omitempty"` +} + +// SetTrackConfigurationResponse type +type SetTrackConfigurationResponse struct { + XMLName xml.Name `xml:"SetTrackConfigurationResponse"` +} + +// CreateRecordingJob type +type CreateRecordingJob struct { + XMLName xml.Name `xml:"tt:CreateRecordingJob"` + + // The initial configuration of the new recording job. + JobConfiguration RecordingJobConfiguration `xml:"tt:JobConfiguration,omitempty"` +} + +// CreateRecordingJobResponse type +type CreateRecordingJobResponse struct { + XMLName xml.Name `xml:"CreateRecordingJobResponse"` + + // The JobToken shall identify the created recording job. + JobToken RecordingJobReference `xml:"JobToken,omitempty"` + + // + // The JobConfiguration structure shall be the configuration as it is used by the device. This may be different from the + // JobConfiguration passed to CreateRecordingJob. + JobConfiguration RecordingJobConfiguration `xml:"JobConfiguration,omitempty"` +} + +// RecordingJobReference type +type RecordingJobReference ReferenceToken + +// RecordingJobConfiguration type +type RecordingJobConfiguration struct { + + // Identifies the recording to which this job shall store the received data. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // The mode of the job. If it is idle, nothing shall happen. If it is active, the device shall try + // to obtain data from the receivers. A client shall use GetRecordingJobState to determine if data transfer is really taking place. + // The only valid values for Mode shall be “Idle” and “Active”. + Mode RecordingJobMode `xml:"tt:Mode,omitempty"` + + // This shall be a non-negative number. If there are multiple recording jobs that store data to + // the same track, the device will only store the data for the recording job with the highest + // priority. The priority is specified per recording job, but the device shall determine the priority + // of each track individually. If there are two recording jobs with the same priority, the device + // shall record the data corresponding to the recording job that was activated the latest. + Priority int32 `xml:"tt:Priority,omitempty"` + + // Source of the recording. + Source []RecordingJobSource `xml:"tt:Source,omitempty"` + + Extension RecordingJobConfigurationExtension `xml:"tt:Extension,omitempty"` + + // This attribute adds an additional requirement for activating the recording job. + // If this optional field is provided the job shall only record if the schedule exists and is active. + // + + ScheduleToken string `xml:"tt:ScheduleToken,attr,omitempty"` +} + +// RecordingJobConfigurationExtension type +type RecordingJobConfigurationExtension struct { +} + +// RecordingJobSource type +type RecordingJobSource struct { + + // This field shall be a reference to the source of the data. The type of the source + // is determined by the attribute Type in the SourceToken structure. If Type is + // http://www.onvif.org/ver10/schema/Receiver, the token is a ReceiverReference. In this case + // the device shall receive the data over the network. If Type is + // http://www.onvif.org/ver10/schema/Profile, the token identifies a media profile, instructing the + // device to obtain data from a profile that exists on the local device. + SourceToken SourceReference `xml:"tt:SourceToken,omitempty"` + + // If this field is TRUE, and if the SourceToken is omitted, the device + // shall create a receiver object (through the receiver service) and assign the + // ReceiverReference to the SourceToken field. When retrieving the RecordingJobConfiguration + // from the device, the AutoCreateReceiver field shall never be present. + AutoCreateReceiver bool `xml:"tt:AutoCreateReceiver,omitempty"` + + // List of tracks associated with the recording. + Tracks []RecordingJobTrack `xml:"tt:Tracks,omitempty"` + + Extension RecordingJobSourceExtension `xml:"tt:Extension,omitempty"` +} + +// RecordingJobTrack type +type RecordingJobTrack struct { + + // If the received RTSP stream contains multiple tracks of the same type, the + // SourceTag differentiates between those Tracks. This field can be ignored in case of recording a local source. + SourceTag string `xml:"tt:SourceTag,omitempty"` + + // The destination is the tracktoken of the track to which the device shall store the + // received data. + Destination TrackReference `xml:"tt:Destination,omitempty"` +} + +// RecordingJobSourceExtension type +type RecordingJobSourceExtension struct { +} + +// RecordingJobMode type +type RecordingJobMode string + +// RecordingJobState type +type RecordingJobState string + +// ModeOfOperation type +type ModeOfOperation string + +// DeleteRecordingJob type +type DeleteRecordingJob struct { + XMLName xml.Name `xml:"tt:DeleteRecordingJob"` + + // The token of the job to be deleted. + JobToken RecordingJobReference `xml:"tt:JobToken,omitempty"` +} + +// DeleteRecordingJobResponse type +type DeleteRecordingJobResponse struct { + XMLName xml.Name `xml:"DeleteRecordingJobResponse"` +} + +// GetRecordingJobs type +type GetRecordingJobs struct { + XMLName xml.Name `xml:"tt:GetRecordingJobs"` +} + +// GetRecordingJobsResponse type +type GetRecordingJobsResponse struct { + XMLName xml.Name `xml:"GetRecordingJobsResponse"` + + // List of recording jobs. + JobItem []GetRecordingJobsResponseItem `xml:"JobItem,omitempty"` +} + +// GetRecordingJobsResponseItem type +type GetRecordingJobsResponseItem struct { + JobToken RecordingJobReference `xml:"JobToken,omitempty"` + + JobConfiguration RecordingJobConfiguration `xml:"JobConfiguration,omitempty"` +} + +// SetRecordingJobConfiguration type +type SetRecordingJobConfiguration struct { + XMLName xml.Name `xml:"tt:SetRecordingJobConfiguration"` + + // Token of the job to be modified. + JobToken RecordingJobReference `xml:"tt:JobToken,omitempty"` + + // New configuration of the recording job. + JobConfiguration RecordingJobConfiguration `xml:"tt:JobConfiguration,omitempty"` +} + +// SetRecordingJobConfigurationResponse type +type SetRecordingJobConfigurationResponse struct { + XMLName xml.Name `xml:"SetRecordingJobConfigurationResponse"` + + // The JobConfiguration structure shall be the configuration + // as it is used by the device. This may be different from the JobConfiguration passed to SetRecordingJobConfiguration. + JobConfiguration RecordingJobConfiguration `xml:"JobConfiguration,omitempty"` +} + +// GetRecordingJobConfiguration type +type GetRecordingJobConfiguration struct { + XMLName xml.Name `xml:"tt:GetRecordingJobConfiguration"` + + // Token of the recording job. + JobToken RecordingJobReference `xml:"tt:JobToken,omitempty"` +} + +// GetRecordingJobConfigurationResponse type +type GetRecordingJobConfigurationResponse struct { + XMLName xml.Name `xml:"GetRecordingJobConfigurationResponse"` + + // Current configuration of the recording job. + JobConfiguration RecordingJobConfiguration `xml:"JobConfiguration,omitempty"` +} + +// SetRecordingJobMode type +type SetRecordingJobMode struct { + XMLName xml.Name `xml:"tt:SetRecordingJobMode"` + + // Token of the recording job. + JobToken RecordingJobReference `xml:"tt:JobToken,omitempty"` + + // The new mode for the recording job. + Mode RecordingJobMode `xml:"tt:Mode,omitempty"` +} + +// SetRecordingJobModeResponse type +type SetRecordingJobModeResponse struct { + XMLName xml.Name `xml:"SetRecordingJobModeResponse"` +} + +// GetRecordingJobState type +type GetRecordingJobState struct { + XMLName xml.Name `xml:"tt:GetRecordingJobState"` + + // Token of the recording job. + JobToken RecordingJobReference `xml:"tt:JobToken,omitempty"` +} + +// GetRecordingJobStateResponse type +type GetRecordingJobStateResponse struct { + XMLName xml.Name `xml:"GetRecordingJobStateResponse"` + + // The current state of the recording job. + State RecordingJobStateInformation `xml:"State,omitempty"` +} + +// RecordingJobStateInformation type +type RecordingJobStateInformation struct { + + // Identification of the recording that the recording job records to. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` + + // Holds the aggregated state over the whole RecordingJobInformation structure. + State RecordingJobState `xml:"tt:State,omitempty"` + + // Identifies the data source of the recording job. + Sources []RecordingJobStateSource `xml:"tt:Sources,omitempty"` + + Extension RecordingJobStateInformationExtension `xml:"tt:Extension,omitempty"` +} + +// RecordingJobStateInformationExtension type +type RecordingJobStateInformationExtension struct { +} + +// RecordingJobStateSource type +type RecordingJobStateSource struct { + + // Identifies the data source of the recording job. + SourceToken SourceReference `xml:"tt:SourceToken,omitempty"` + + // Holds the aggregated state over all substructures of RecordingJobStateSource. + State RecordingJobState `xml:"tt:State,omitempty"` + + // List of track items. + Tracks RecordingJobStateTracks `xml:"tt:Tracks,omitempty"` +} + +// RecordingJobStateTracks type +type RecordingJobStateTracks struct { + Track []RecordingJobStateTrack `xml:"tt:Track,omitempty"` +} + +// RecordingJobStateTrack type +type RecordingJobStateTrack struct { + + // Identifies the track of the data source that provides the data. + SourceTag string `xml:"tt:SourceTag,omitempty"` + + // Indicates the destination track. + Destination TrackReference `xml:"tt:Destination,omitempty"` + + // Optionally holds an implementation defined string value that describes the error. + // The string should be in the English language. + Error string `xml:"tt:Error,omitempty"` + + // Provides the job state of the track. The valid + // values of state shall be “Idle”, “Active” and “Error”. If state equals “Error”, the Error field may be filled in with an implementation defined value. + State RecordingJobState `xml:"tt:State,omitempty"` +} + +// GetRecordingOptions type +type GetRecordingOptions struct { + XMLName xml.Name `xml:"tt:GetRecordingOptions"` + + // Token of the recording. + RecordingToken RecordingReference `xml:"tt:RecordingToken,omitempty"` +} + +// GetRecordingOptionsResponse type +type GetRecordingOptionsResponse struct { + XMLName xml.Name `xml:"GetRecordingOptionsResponse"` + + // Configuration of the recording. + Options RecordingOptions `xml:"Options,omitempty"` +} + +// ExportRecordedData type +type ExportRecordedData struct { + XMLName xml.Name `xml:"tt:ExportRecordedData"` + + // Optional parameter that specifies start time for the exporting. + StartPoint string `xml:"tt:StartPoint,omitempty"` + + // Optional parameter that specifies end time for the exporting. + EndPoint string `xml:"tt:EndPoint,omitempty"` + + // Indicates the selection criterion on the existing recordings. . + SearchScope SearchScope `xml:"tt:SearchScope,omitempty"` + + // Indicates which export file format to be used. + FileFormat string `xml:"tt:FileFormat,omitempty"` + + // Indicates the target storage and relative directory path. + StorageDestination StorageReferencePath `xml:"tt:StorageDestination,omitempty"` +} + +// StorageReferencePath type +type StorageReferencePath struct { + + // identifier of an existing Storage Configuration. + StorageToken ReferenceToken `xml:"tt:StorageToken,omitempty"` + + // gives the relative directory path on the storage + RelativePath string `xml:"tt:RelativePath,omitempty"` + + Extension StorageReferencePathExtension `xml:"tt:Extension,omitempty"` +} + +// StorageReferencePathExtension type +type StorageReferencePathExtension struct { +} + +// ExportRecordedDataResponse type +type ExportRecordedDataResponse struct { + XMLName xml.Name `xml:"ExportRecordedDataResponse"` + + // Unique operation token for client to associate the relevant events. + OperationToken ReferenceToken `xml:"OperationToken,omitempty"` + + // List of exported file names. The device can also use AsyncronousOperationStatus event to publish this list. + FileNames []string `xml:"FileNames,omitempty"` + + Extension struct { + } `xml:"Extension,omitempty"` +} + +// StopExportRecordedData type +type StopExportRecordedData struct { + XMLName xml.Name `xml:"tt:StopExportRecordedData"` + + // Unique ExportRecordedData operation token + OperationToken ReferenceToken `xml:"tt:OperationToken,omitempty"` +} + +// StopExportRecordedDataResponse type +type StopExportRecordedDataResponse struct { + XMLName xml.Name `xml:"StopExportRecordedDataResponse"` + + // Progress percentage of ExportRecordedData operation. + Progress float32 `xml:"Progress,omitempty"` + + FileProgressStatus ArrayOfFileProgress `xml:"FileProgressStatus,omitempty"` +} + +// ArrayOfFileProgress type +type ArrayOfFileProgress struct { + + // Exported file name and export progress information + FileProgress []FileProgress `xml:"tt:FileProgress,omitempty"` + + Extension ArrayOfFileProgressExtension `xml:"tt:Extension,omitempty"` +} + +// FileProgress type +type FileProgress struct { + + // Exported file name + FileName string `xml:"tt:FileName,omitempty"` + + // Normalized percentage completion for uploading the exported file + Progress float32 `xml:"tt:Progress,omitempty"` +} + +// ArrayOfFileProgressExtension type +type ArrayOfFileProgressExtension struct { +} + +// GetExportRecordedDataState type +type GetExportRecordedDataState struct { + XMLName xml.Name `xml:"tt:GetExportRecordedDataState"` + + // Unique ExportRecordedData operation token + OperationToken ReferenceToken `xml:"tt:OperationToken,omitempty"` +} + +// GetExportRecordedDataStateResponse type +type GetExportRecordedDataStateResponse struct { + XMLName xml.Name `xml:"GetExportRecordedDataStateResponse"` + + // Progress percentage of ExportRecordedData operation. + Progress float32 `xml:"Progress,omitempty"` + + FileProgressStatus ArrayOfFileProgress `xml:"FileProgressStatus,omitempty"` +} + +// Capabilities type +type Capabilities struct { + + // Indication if the device supports dynamic creation and deletion of recordings + + DynamicRecordings bool `xml:"tt:DynamicRecordings,attr,omitempty"` + + // Indication if the device supports dynamic creation and deletion of tracks + + DynamicTracks bool `xml:"tt:DynamicTracks,attr,omitempty"` + + // Indication which encodings are supported for recording. The list may contain one or more enumeration values of tt:VideoEncoding and tt:AudioEncoding. For encodings that are neither defined in tt:VideoEncoding nor tt:AudioEncoding the device shall use the defintions. Note, that a device without audio support shall not return audio encodings. + + Encoding EncodingTypes `xml:"tt:Encoding,attr,omitempty"` + + // Maximum supported bit rate for all tracks of a recording in kBit/s. + + MaxRate float32 `xml:"tt:MaxRate,attr,omitempty"` + + // Maximum supported bit rate for all recordings in kBit/s. + + MaxTotalRate float32 `xml:"tt:MaxTotalRate,attr,omitempty"` + + // Maximum number of recordings supported. (Integer values only.) + + MaxRecordings float32 `xml:"tt:MaxRecordings,attr,omitempty"` + + // Maximum total number of supported recording jobs by the device. + + MaxRecordingJobs int32 `xml:"tt:MaxRecordingJobs,attr,omitempty"` + + // Indication if the device supports the GetRecordingOptions command. + + Options bool `xml:"tt:Options,attr,omitempty"` + + // Indication if the device supports recording metadata. + + MetadataRecording bool `xml:"tt:MetadataRecording,attr,omitempty"` + + // + // Indication that the device supports ExportRecordedData command for the listed export file formats. + // The list shall return at least one export file format value. The value of 'ONVIF' refers to + // ONVIF Export File Format specification. + // + + SupportedExportFileFormats onvif.StringAttrList `xml:"tt:SupportedExportFileFormats,attr,omitempty"` +} + +// RecordingOptions type +type RecordingOptions struct { + Job JobOptions `xml:"tt:Job,omitempty"` + + Track TrackOptions `xml:"tt:Track,omitempty"` +} + +// JobOptions type +type JobOptions struct { + + // Number of spare jobs that can be created for the recording. + + Spare int32 `xml:"tt:Spare,attr,omitempty"` + + // A device that supports recording of a restricted set of Media/Media2 Service Profiles returns the list of profiles that can be recorded on the given Recording. + + CompatibleSources onvif.StringAttrList `xml:"tt:CompatibleSources,attr,omitempty"` +} + +// TrackOptions type +type TrackOptions struct { + + // Total spare number of tracks that can be added to this recording. + + SpareTotal int32 `xml:"tt:SpareTotal,attr,omitempty"` + + // Number of spare Video tracks that can be added to this recording. + + SpareVideo int32 `xml:"tt:SpareVideo,attr,omitempty"` + + // Number of spare Aduio tracks that can be added to this recording. + + SpareAudio int32 `xml:"tt:SpareAudio,attr,omitempty"` + + // Number of spare Metadata tracks that can be added to this recording. + + SpareMetadata int32 `xml:"tt:SpareMetadata,attr,omitempty"` +} + +// SearchScope type +type SearchScope struct { + + // A list of sources that are included in the scope. If this list is included, only data from one of these sources shall be searched. + IncludedSources []SourceReference `xml:"tt:IncludedSources,omitempty"` + + // A list of recordings that are included in the scope. If this list is included, only data from one of these recordings shall be searched. + IncludedRecordings []RecordingReference `xml:"tt:IncludedRecordings,omitempty"` + + // An xpath expression used to specify what recordings to search. Only those recordings with an RecordingInformation structure that matches the filter shall be searched. + RecordingInformationFilter XPathExpression `xml:"tt:RecordingInformationFilter,omitempty"` + + // Extension point + Extension SearchScopeExtension `xml:"tt:Extension,omitempty"` +} + +// XPathExpression type +type XPathExpression string + +// SourceReference type +type SourceReference struct { + Token ReferenceToken `xml:"tt:Token,omitempty"` + + Type xsd.AnyURI `xml:"tt:Type,attr,omitempty"` +} + +// RecordingReference type +type RecordingReference ReferenceToken + +// TrackReference type +type TrackReference ReferenceToken + +// ReferenceToken type +type ReferenceToken string + +// SearchScopeExtension type +type SearchScopeExtension struct { +} diff --git a/sdk/codegen/main.go b/sdk/codegen/main.go deleted file mode 100644 index 9884c99..0000000 --- a/sdk/codegen/main.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package main - -import ( - "flag" - "log" - "os" - "text/template" -) - -var mainTemplate = `// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package {{.Package}} - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/{{.StructPackage}}" -) - -// Call_{{.TypeRequest}} forwards the call to dev.CallMethod() then parses the payload of the reply as a {{.TypeReply}}. -func Call_{{.TypeRequest}}(ctx context.Context, dev *onvif.Device, request {{.StructPackage}}.{{.TypeRequest}}) ({{.StructPackage}}.{{.TypeReply}}, error) { - type Envelope struct { - Header struct{} - Body struct { - {{.TypeReply}} {{.StructPackage}}.{{.TypeReply}} - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.{{.TypeReply}}, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "{{.TypeRequest}}") - return reply.Body.{{.TypeReply}}, errors.Annotate(err, "reply") - } -} -` - -type parserEnv struct { - Package string - StructPackage string - TypeReply string - TypeRequest string -} - -func main() { - flag.Parse() - env := parserEnv{ - Package: flag.Arg(0), - StructPackage: flag.Arg(1), - TypeRequest: flag.Arg(2), - TypeReply: flag.Arg(2) + "Response", - } - - log.Println(env) - - body, err := template.New("body").Parse(mainTemplate) - if err != nil { - log.Fatalln(err) - } - - fout, err := os.Create(env.TypeRequest + "_auto.go") - if err != nil { - log.Fatalln(err) - } - defer fout.Close() - - err = body.Execute(fout, &env) - if err != nil { - log.Fatalln(err) - } -} diff --git a/sdk/device/AddIPAddressFilter_auto.go b/sdk/device/AddIPAddressFilter_auto.go deleted file mode 100644 index 15cd4df..0000000 --- a/sdk/device/AddIPAddressFilter_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_AddIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a AddIPAddressFilterResponse. -func Call_AddIPAddressFilter(ctx context.Context, dev *onvif.Device, request device.AddIPAddressFilter) (device.AddIPAddressFilterResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddIPAddressFilterResponse device.AddIPAddressFilterResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddIPAddressFilterResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddIPAddressFilter") - return reply.Body.AddIPAddressFilterResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/AddScopes_auto.go b/sdk/device/AddScopes_auto.go deleted file mode 100644 index 0f765f4..0000000 --- a/sdk/device/AddScopes_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_AddScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a AddScopesResponse. -func Call_AddScopes(ctx context.Context, dev *onvif.Device, request device.AddScopes) (device.AddScopesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddScopesResponse device.AddScopesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddScopesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddScopes") - return reply.Body.AddScopesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/CreateCertificate_auto.go b/sdk/device/CreateCertificate_auto.go deleted file mode 100644 index 1a733d5..0000000 --- a/sdk/device/CreateCertificate_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_CreateCertificate forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateCertificateResponse. -func Call_CreateCertificate(ctx context.Context, dev *onvif.Device, request device.CreateCertificate) (device.CreateCertificateResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreateCertificateResponse device.CreateCertificateResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreateCertificateResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreateCertificate") - return reply.Body.CreateCertificateResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/CreateDot1XConfiguration_auto.go b/sdk/device/CreateDot1XConfiguration_auto.go deleted file mode 100644 index c34ec64..0000000 --- a/sdk/device/CreateDot1XConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_CreateDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateDot1XConfigurationResponse. -func Call_CreateDot1XConfiguration(ctx context.Context, dev *onvif.Device, request device.CreateDot1XConfiguration) (device.CreateDot1XConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreateDot1XConfigurationResponse device.CreateDot1XConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreateDot1XConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreateDot1XConfiguration") - return reply.Body.CreateDot1XConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/CreateStorageConfiguration_auto.go b/sdk/device/CreateStorageConfiguration_auto.go deleted file mode 100644 index 542f374..0000000 --- a/sdk/device/CreateStorageConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_CreateStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateStorageConfigurationResponse. -func Call_CreateStorageConfiguration(ctx context.Context, dev *onvif.Device, request device.CreateStorageConfiguration) (device.CreateStorageConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreateStorageConfigurationResponse device.CreateStorageConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreateStorageConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreateStorageConfiguration") - return reply.Body.CreateStorageConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/CreateUsers_auto.go b/sdk/device/CreateUsers_auto.go deleted file mode 100644 index a938bed..0000000 --- a/sdk/device/CreateUsers_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_CreateUsers forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateUsersResponse. -func Call_CreateUsers(ctx context.Context, dev *onvif.Device, request device.CreateUsers) (device.CreateUsersResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreateUsersResponse device.CreateUsersResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreateUsersResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreateUsers") - return reply.Body.CreateUsersResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/DeleteCertificates_auto.go b/sdk/device/DeleteCertificates_auto.go deleted file mode 100644 index 5aedbe8..0000000 --- a/sdk/device/DeleteCertificates_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_DeleteCertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteCertificatesResponse. -func Call_DeleteCertificates(ctx context.Context, dev *onvif.Device, request device.DeleteCertificates) (device.DeleteCertificatesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteCertificatesResponse device.DeleteCertificatesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteCertificatesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteCertificates") - return reply.Body.DeleteCertificatesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/DeleteDot1XConfiguration_auto.go b/sdk/device/DeleteDot1XConfiguration_auto.go deleted file mode 100644 index 51dea1f..0000000 --- a/sdk/device/DeleteDot1XConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_DeleteDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteDot1XConfigurationResponse. -func Call_DeleteDot1XConfiguration(ctx context.Context, dev *onvif.Device, request device.DeleteDot1XConfiguration) (device.DeleteDot1XConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteDot1XConfigurationResponse device.DeleteDot1XConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteDot1XConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteDot1XConfiguration") - return reply.Body.DeleteDot1XConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/DeleteGeoLocation_auto.go b/sdk/device/DeleteGeoLocation_auto.go deleted file mode 100644 index 13a2af7..0000000 --- a/sdk/device/DeleteGeoLocation_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_DeleteGeoLocation forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteGeoLocationResponse. -func Call_DeleteGeoLocation(ctx context.Context, dev *onvif.Device, request device.DeleteGeoLocation) (device.DeleteGeoLocationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteGeoLocationResponse device.DeleteGeoLocationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteGeoLocationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteGeoLocation") - return reply.Body.DeleteGeoLocationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/DeleteStorageConfiguration_auto.go b/sdk/device/DeleteStorageConfiguration_auto.go deleted file mode 100644 index 7866d53..0000000 --- a/sdk/device/DeleteStorageConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_DeleteStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteStorageConfigurationResponse. -func Call_DeleteStorageConfiguration(ctx context.Context, dev *onvif.Device, request device.DeleteStorageConfiguration) (device.DeleteStorageConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteStorageConfigurationResponse device.DeleteStorageConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteStorageConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteStorageConfiguration") - return reply.Body.DeleteStorageConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/DeleteUsers_auto.go b/sdk/device/DeleteUsers_auto.go deleted file mode 100644 index 786a5a4..0000000 --- a/sdk/device/DeleteUsers_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_DeleteUsers forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteUsersResponse. -func Call_DeleteUsers(ctx context.Context, dev *onvif.Device, request device.DeleteUsers) (device.DeleteUsersResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteUsersResponse device.DeleteUsersResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteUsersResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteUsers") - return reply.Body.DeleteUsersResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetAccessPolicy_auto.go b/sdk/device/GetAccessPolicy_auto.go deleted file mode 100644 index 66ac1ee..0000000 --- a/sdk/device/GetAccessPolicy_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetAccessPolicy forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAccessPolicyResponse. -func Call_GetAccessPolicy(ctx context.Context, dev *onvif.Device, request device.GetAccessPolicy) (device.GetAccessPolicyResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAccessPolicyResponse device.GetAccessPolicyResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAccessPolicyResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAccessPolicy") - return reply.Body.GetAccessPolicyResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetCACertificates_auto.go b/sdk/device/GetCACertificates_auto.go deleted file mode 100644 index 160a8c2..0000000 --- a/sdk/device/GetCACertificates_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetCACertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCACertificatesResponse. -func Call_GetCACertificates(ctx context.Context, dev *onvif.Device, request device.GetCACertificates) (device.GetCACertificatesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCACertificatesResponse device.GetCACertificatesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCACertificatesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCACertificates") - return reply.Body.GetCACertificatesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetCapabilities_auto.go b/sdk/device/GetCapabilities_auto.go deleted file mode 100644 index 8df95ee..0000000 --- a/sdk/device/GetCapabilities_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCapabilitiesResponse. -func Call_GetCapabilities(ctx context.Context, dev *onvif.Device, request device.GetCapabilities) (device.GetCapabilitiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCapabilitiesResponse device.GetCapabilitiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCapabilitiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCapabilities") - return reply.Body.GetCapabilitiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetCertificateInformation_auto.go b/sdk/device/GetCertificateInformation_auto.go deleted file mode 100644 index f91541b..0000000 --- a/sdk/device/GetCertificateInformation_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetCertificateInformation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCertificateInformationResponse. -func Call_GetCertificateInformation(ctx context.Context, dev *onvif.Device, request device.GetCertificateInformation) (device.GetCertificateInformationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCertificateInformationResponse device.GetCertificateInformationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCertificateInformationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCertificateInformation") - return reply.Body.GetCertificateInformationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetCertificatesStatus_auto.go b/sdk/device/GetCertificatesStatus_auto.go deleted file mode 100644 index 1981b6b..0000000 --- a/sdk/device/GetCertificatesStatus_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetCertificatesStatus forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCertificatesStatusResponse. -func Call_GetCertificatesStatus(ctx context.Context, dev *onvif.Device, request device.GetCertificatesStatus) (device.GetCertificatesStatusResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCertificatesStatusResponse device.GetCertificatesStatusResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCertificatesStatusResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCertificatesStatus") - return reply.Body.GetCertificatesStatusResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetCertificates_auto.go b/sdk/device/GetCertificates_auto.go deleted file mode 100644 index 7143c7d..0000000 --- a/sdk/device/GetCertificates_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetCertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCertificatesResponse. -func Call_GetCertificates(ctx context.Context, dev *onvif.Device, request device.GetCertificates) (device.GetCertificatesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCertificatesResponse device.GetCertificatesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCertificatesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCertificates") - return reply.Body.GetCertificatesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetClientCertificateMode_auto.go b/sdk/device/GetClientCertificateMode_auto.go deleted file mode 100644 index 9f3ba10..0000000 --- a/sdk/device/GetClientCertificateMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetClientCertificateMode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetClientCertificateModeResponse. -func Call_GetClientCertificateMode(ctx context.Context, dev *onvif.Device, request device.GetClientCertificateMode) (device.GetClientCertificateModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetClientCertificateModeResponse device.GetClientCertificateModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetClientCertificateModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetClientCertificateMode") - return reply.Body.GetClientCertificateModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDNS_auto.go b/sdk/device/GetDNS_auto.go deleted file mode 100644 index 68f60d2..0000000 --- a/sdk/device/GetDNS_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDNSResponse. -func Call_GetDNS(ctx context.Context, dev *onvif.Device, request device.GetDNS) (device.GetDNSResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDNSResponse device.GetDNSResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDNSResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDNS") - return reply.Body.GetDNSResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDPAddresses_auto.go b/sdk/device/GetDPAddresses_auto.go deleted file mode 100644 index 6b12e4d..0000000 --- a/sdk/device/GetDPAddresses_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDPAddresses forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDPAddressesResponse. -func Call_GetDPAddresses(ctx context.Context, dev *onvif.Device, request device.GetDPAddresses) (device.GetDPAddressesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDPAddressesResponse device.GetDPAddressesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDPAddressesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDPAddresses") - return reply.Body.GetDPAddressesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDeviceInformation_auto.go b/sdk/device/GetDeviceInformation_auto.go deleted file mode 100644 index 9511f7e..0000000 --- a/sdk/device/GetDeviceInformation_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDeviceInformation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDeviceInformationResponse. -func Call_GetDeviceInformation(ctx context.Context, dev *onvif.Device, request device.GetDeviceInformation) (device.GetDeviceInformationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDeviceInformationResponse device.GetDeviceInformationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDeviceInformationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDeviceInformation") - return reply.Body.GetDeviceInformationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDiscoveryMode_auto.go b/sdk/device/GetDiscoveryMode_auto.go deleted file mode 100644 index 81c8b2b..0000000 --- a/sdk/device/GetDiscoveryMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDiscoveryModeResponse. -func Call_GetDiscoveryMode(ctx context.Context, dev *onvif.Device, request device.GetDiscoveryMode) (device.GetDiscoveryModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDiscoveryModeResponse device.GetDiscoveryModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDiscoveryModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDiscoveryMode") - return reply.Body.GetDiscoveryModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDot11Capabilities_auto.go b/sdk/device/GetDot11Capabilities_auto.go deleted file mode 100644 index 68cfc0f..0000000 --- a/sdk/device/GetDot11Capabilities_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDot11Capabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot11CapabilitiesResponse. -func Call_GetDot11Capabilities(ctx context.Context, dev *onvif.Device, request device.GetDot11Capabilities) (device.GetDot11CapabilitiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDot11CapabilitiesResponse device.GetDot11CapabilitiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDot11CapabilitiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDot11Capabilities") - return reply.Body.GetDot11CapabilitiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDot11Status_auto.go b/sdk/device/GetDot11Status_auto.go deleted file mode 100644 index 7256878..0000000 --- a/sdk/device/GetDot11Status_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDot11Status forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot11StatusResponse. -func Call_GetDot11Status(ctx context.Context, dev *onvif.Device, request device.GetDot11Status) (device.GetDot11StatusResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDot11StatusResponse device.GetDot11StatusResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDot11StatusResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDot11Status") - return reply.Body.GetDot11StatusResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDot1XConfiguration_auto.go b/sdk/device/GetDot1XConfiguration_auto.go deleted file mode 100644 index 19e3e64..0000000 --- a/sdk/device/GetDot1XConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot1XConfigurationResponse. -func Call_GetDot1XConfiguration(ctx context.Context, dev *onvif.Device, request device.GetDot1XConfiguration) (device.GetDot1XConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDot1XConfigurationResponse device.GetDot1XConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDot1XConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDot1XConfiguration") - return reply.Body.GetDot1XConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDot1XConfigurations_auto.go b/sdk/device/GetDot1XConfigurations_auto.go deleted file mode 100644 index 3b49779..0000000 --- a/sdk/device/GetDot1XConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDot1XConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDot1XConfigurationsResponse. -func Call_GetDot1XConfigurations(ctx context.Context, dev *onvif.Device, request device.GetDot1XConfigurations) (device.GetDot1XConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDot1XConfigurationsResponse device.GetDot1XConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDot1XConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDot1XConfigurations") - return reply.Body.GetDot1XConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetDynamicDNS_auto.go b/sdk/device/GetDynamicDNS_auto.go deleted file mode 100644 index 7cf603b..0000000 --- a/sdk/device/GetDynamicDNS_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetDynamicDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a GetDynamicDNSResponse. -func Call_GetDynamicDNS(ctx context.Context, dev *onvif.Device, request device.GetDynamicDNS) (device.GetDynamicDNSResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetDynamicDNSResponse device.GetDynamicDNSResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetDynamicDNSResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetDynamicDNS") - return reply.Body.GetDynamicDNSResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetEndpointReference_auto.go b/sdk/device/GetEndpointReference_auto.go deleted file mode 100644 index 2261c52..0000000 --- a/sdk/device/GetEndpointReference_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetEndpointReference forwards the call to dev.CallMethod() then parses the payload of the reply as a GetEndpointReferenceResponse. -func Call_GetEndpointReference(ctx context.Context, dev *onvif.Device, request device.GetEndpointReference) (device.GetEndpointReferenceResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetEndpointReferenceResponse device.GetEndpointReferenceResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetEndpointReferenceResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetEndpointReference") - return reply.Body.GetEndpointReferenceResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetGeoLocation_auto.go b/sdk/device/GetGeoLocation_auto.go deleted file mode 100644 index c734c7f..0000000 --- a/sdk/device/GetGeoLocation_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetGeoLocation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetGeoLocationResponse. -func Call_GetGeoLocation(ctx context.Context, dev *onvif.Device, request device.GetGeoLocation) (device.GetGeoLocationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetGeoLocationResponse device.GetGeoLocationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetGeoLocationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetGeoLocation") - return reply.Body.GetGeoLocationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetHostname_auto.go b/sdk/device/GetHostname_auto.go deleted file mode 100644 index 79c67e6..0000000 --- a/sdk/device/GetHostname_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetHostname forwards the call to dev.CallMethod() then parses the payload of the reply as a GetHostnameResponse. -func Call_GetHostname(ctx context.Context, dev *onvif.Device, request device.GetHostname) (device.GetHostnameResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetHostnameResponse device.GetHostnameResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetHostnameResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetHostname") - return reply.Body.GetHostnameResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetIPAddressFilter_auto.go b/sdk/device/GetIPAddressFilter_auto.go deleted file mode 100644 index 9c7fd95..0000000 --- a/sdk/device/GetIPAddressFilter_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a GetIPAddressFilterResponse. -func Call_GetIPAddressFilter(ctx context.Context, dev *onvif.Device, request device.GetIPAddressFilter) (device.GetIPAddressFilterResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetIPAddressFilterResponse device.GetIPAddressFilterResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetIPAddressFilterResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetIPAddressFilter") - return reply.Body.GetIPAddressFilterResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetNTP_auto.go b/sdk/device/GetNTP_auto.go deleted file mode 100644 index 8851044..0000000 --- a/sdk/device/GetNTP_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetNTP forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNTPResponse. -func Call_GetNTP(ctx context.Context, dev *onvif.Device, request device.GetNTP) (device.GetNTPResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetNTPResponse device.GetNTPResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetNTPResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetNTP") - return reply.Body.GetNTPResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetNetworkDefaultGateway_auto.go b/sdk/device/GetNetworkDefaultGateway_auto.go deleted file mode 100644 index 3f2dff4..0000000 --- a/sdk/device/GetNetworkDefaultGateway_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetNetworkDefaultGateway forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNetworkDefaultGatewayResponse. -func Call_GetNetworkDefaultGateway(ctx context.Context, dev *onvif.Device, request device.GetNetworkDefaultGateway) (device.GetNetworkDefaultGatewayResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetNetworkDefaultGatewayResponse device.GetNetworkDefaultGatewayResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetNetworkDefaultGatewayResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetNetworkDefaultGateway") - return reply.Body.GetNetworkDefaultGatewayResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetNetworkInterfaces_auto.go b/sdk/device/GetNetworkInterfaces_auto.go deleted file mode 100644 index a19be90..0000000 --- a/sdk/device/GetNetworkInterfaces_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetNetworkInterfaces forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNetworkInterfacesResponse. -func Call_GetNetworkInterfaces(ctx context.Context, dev *onvif.Device, request device.GetNetworkInterfaces) (device.GetNetworkInterfacesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetNetworkInterfacesResponse device.GetNetworkInterfacesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetNetworkInterfacesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetNetworkInterfaces") - return reply.Body.GetNetworkInterfacesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetNetworkProtocols_auto.go b/sdk/device/GetNetworkProtocols_auto.go deleted file mode 100644 index b922d68..0000000 --- a/sdk/device/GetNetworkProtocols_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetNetworkProtocols forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNetworkProtocolsResponse. -func Call_GetNetworkProtocols(ctx context.Context, dev *onvif.Device, request device.GetNetworkProtocols) (device.GetNetworkProtocolsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetNetworkProtocolsResponse device.GetNetworkProtocolsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetNetworkProtocolsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetNetworkProtocols") - return reply.Body.GetNetworkProtocolsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetPkcs10Request_auto.go b/sdk/device/GetPkcs10Request_auto.go deleted file mode 100644 index d4cc7e8..0000000 --- a/sdk/device/GetPkcs10Request_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetPkcs10Request forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPkcs10RequestResponse. -func Call_GetPkcs10Request(ctx context.Context, dev *onvif.Device, request device.GetPkcs10Request) (device.GetPkcs10RequestResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetPkcs10RequestResponse device.GetPkcs10RequestResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetPkcs10RequestResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetPkcs10Request") - return reply.Body.GetPkcs10RequestResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetRelayOutputs_auto.go b/sdk/device/GetRelayOutputs_auto.go deleted file mode 100644 index 732ab49..0000000 --- a/sdk/device/GetRelayOutputs_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetRelayOutputs forwards the call to dev.CallMethod() then parses the payload of the reply as a GetRelayOutputsResponse. -func Call_GetRelayOutputs(ctx context.Context, dev *onvif.Device, request device.GetRelayOutputs) (device.GetRelayOutputsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetRelayOutputsResponse device.GetRelayOutputsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetRelayOutputsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetRelayOutputs") - return reply.Body.GetRelayOutputsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetRemoteDiscoveryMode_auto.go b/sdk/device/GetRemoteDiscoveryMode_auto.go deleted file mode 100644 index 263fa23..0000000 --- a/sdk/device/GetRemoteDiscoveryMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetRemoteDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetRemoteDiscoveryModeResponse. -func Call_GetRemoteDiscoveryMode(ctx context.Context, dev *onvif.Device, request device.GetRemoteDiscoveryMode) (device.GetRemoteDiscoveryModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetRemoteDiscoveryModeResponse device.GetRemoteDiscoveryModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetRemoteDiscoveryModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetRemoteDiscoveryMode") - return reply.Body.GetRemoteDiscoveryModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetRemoteUser_auto.go b/sdk/device/GetRemoteUser_auto.go deleted file mode 100644 index bdab0b1..0000000 --- a/sdk/device/GetRemoteUser_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetRemoteUser forwards the call to dev.CallMethod() then parses the payload of the reply as a GetRemoteUserResponse. -func Call_GetRemoteUser(ctx context.Context, dev *onvif.Device, request device.GetRemoteUser) (device.GetRemoteUserResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetRemoteUserResponse device.GetRemoteUserResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetRemoteUserResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetRemoteUser") - return reply.Body.GetRemoteUserResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetScopes_auto.go b/sdk/device/GetScopes_auto.go deleted file mode 100644 index 9f68ecb..0000000 --- a/sdk/device/GetScopes_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a GetScopesResponse. -func Call_GetScopes(ctx context.Context, dev *onvif.Device, request device.GetScopes) (device.GetScopesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetScopesResponse device.GetScopesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetScopesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetScopes") - return reply.Body.GetScopesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetServiceCapabilities_auto.go b/sdk/device/GetServiceCapabilities_auto.go deleted file mode 100644 index 54f970c..0000000 --- a/sdk/device/GetServiceCapabilities_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. -func Call_GetServiceCapabilities(ctx context.Context, dev *onvif.Device, request device.GetServiceCapabilities) (device.GetServiceCapabilitiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetServiceCapabilitiesResponse device.GetServiceCapabilitiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetServiceCapabilities") - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetServices_auto.go b/sdk/device/GetServices_auto.go deleted file mode 100644 index bfbef44..0000000 --- a/sdk/device/GetServices_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetServices forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServicesResponse. -func Call_GetServices(ctx context.Context, dev *onvif.Device, request device.GetServices) (device.GetServicesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetServicesResponse device.GetServicesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetServicesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetServices") - return reply.Body.GetServicesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetStorageConfiguration_auto.go b/sdk/device/GetStorageConfiguration_auto.go deleted file mode 100644 index ceac17c..0000000 --- a/sdk/device/GetStorageConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStorageConfigurationResponse. -func Call_GetStorageConfiguration(ctx context.Context, dev *onvif.Device, request device.GetStorageConfiguration) (device.GetStorageConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetStorageConfigurationResponse device.GetStorageConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetStorageConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetStorageConfiguration") - return reply.Body.GetStorageConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetStorageConfigurations_auto.go b/sdk/device/GetStorageConfigurations_auto.go deleted file mode 100644 index 502be95..0000000 --- a/sdk/device/GetStorageConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetStorageConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStorageConfigurationsResponse. -func Call_GetStorageConfigurations(ctx context.Context, dev *onvif.Device, request device.GetStorageConfigurations) (device.GetStorageConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetStorageConfigurationsResponse device.GetStorageConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetStorageConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetStorageConfigurations") - return reply.Body.GetStorageConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetSystemBackup_auto.go b/sdk/device/GetSystemBackup_auto.go deleted file mode 100644 index 37884d0..0000000 --- a/sdk/device/GetSystemBackup_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetSystemBackup forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemBackupResponse. -func Call_GetSystemBackup(ctx context.Context, dev *onvif.Device, request device.GetSystemBackup) (device.GetSystemBackupResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetSystemBackupResponse device.GetSystemBackupResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetSystemBackupResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetSystemBackup") - return reply.Body.GetSystemBackupResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetSystemDateAndTime_auto.go b/sdk/device/GetSystemDateAndTime_auto.go deleted file mode 100644 index 6c2d265..0000000 --- a/sdk/device/GetSystemDateAndTime_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetSystemDateAndTime forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemDateAndTimeResponse. -func Call_GetSystemDateAndTime(ctx context.Context, dev *onvif.Device, request device.GetSystemDateAndTime) (device.GetSystemDateAndTimeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetSystemDateAndTimeResponse device.GetSystemDateAndTimeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetSystemDateAndTimeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetSystemDateAndTime") - return reply.Body.GetSystemDateAndTimeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetSystemLog_auto.go b/sdk/device/GetSystemLog_auto.go deleted file mode 100644 index 87b9945..0000000 --- a/sdk/device/GetSystemLog_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetSystemLog forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemLogResponse. -func Call_GetSystemLog(ctx context.Context, dev *onvif.Device, request device.GetSystemLog) (device.GetSystemLogResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetSystemLogResponse device.GetSystemLogResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetSystemLogResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetSystemLog") - return reply.Body.GetSystemLogResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetSystemSupportInformation_auto.go b/sdk/device/GetSystemSupportInformation_auto.go deleted file mode 100644 index 16593ad..0000000 --- a/sdk/device/GetSystemSupportInformation_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetSystemSupportInformation forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemSupportInformationResponse. -func Call_GetSystemSupportInformation(ctx context.Context, dev *onvif.Device, request device.GetSystemSupportInformation) (device.GetSystemSupportInformationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetSystemSupportInformationResponse device.GetSystemSupportInformationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetSystemSupportInformationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetSystemSupportInformation") - return reply.Body.GetSystemSupportInformationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetSystemUris_auto.go b/sdk/device/GetSystemUris_auto.go deleted file mode 100644 index 1eec175..0000000 --- a/sdk/device/GetSystemUris_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetSystemUris forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSystemUrisResponse. -func Call_GetSystemUris(ctx context.Context, dev *onvif.Device, request device.GetSystemUris) (device.GetSystemUrisResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetSystemUrisResponse device.GetSystemUrisResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetSystemUrisResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetSystemUris") - return reply.Body.GetSystemUrisResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetUsers_auto.go b/sdk/device/GetUsers_auto.go deleted file mode 100644 index 95d863d..0000000 --- a/sdk/device/GetUsers_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetUsers forwards the call to dev.CallMethod() then parses the payload of the reply as a GetUsersResponse. -func Call_GetUsers(ctx context.Context, dev *onvif.Device, request device.GetUsers) (device.GetUsersResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetUsersResponse device.GetUsersResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetUsersResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetUsers") - return reply.Body.GetUsersResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetWsdlUrl_auto.go b/sdk/device/GetWsdlUrl_auto.go deleted file mode 100644 index a010bcc..0000000 --- a/sdk/device/GetWsdlUrl_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetWsdlUrl forwards the call to dev.CallMethod() then parses the payload of the reply as a GetWsdlUrlResponse. -func Call_GetWsdlUrl(ctx context.Context, dev *onvif.Device, request device.GetWsdlUrl) (device.GetWsdlUrlResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetWsdlUrlResponse device.GetWsdlUrlResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetWsdlUrlResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetWsdlUrl") - return reply.Body.GetWsdlUrlResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/GetZeroConfiguration_auto.go b/sdk/device/GetZeroConfiguration_auto.go deleted file mode 100644 index 02887a8..0000000 --- a/sdk/device/GetZeroConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_GetZeroConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetZeroConfigurationResponse. -func Call_GetZeroConfiguration(ctx context.Context, dev *onvif.Device, request device.GetZeroConfiguration) (device.GetZeroConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetZeroConfigurationResponse device.GetZeroConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetZeroConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetZeroConfiguration") - return reply.Body.GetZeroConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/LoadCACertificates_auto.go b/sdk/device/LoadCACertificates_auto.go deleted file mode 100644 index 8a15da6..0000000 --- a/sdk/device/LoadCACertificates_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_LoadCACertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a LoadCACertificatesResponse. -func Call_LoadCACertificates(ctx context.Context, dev *onvif.Device, request device.LoadCACertificates) (device.LoadCACertificatesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - LoadCACertificatesResponse device.LoadCACertificatesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.LoadCACertificatesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "LoadCACertificates") - return reply.Body.LoadCACertificatesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/LoadCertificateWithPrivateKey_auto.go b/sdk/device/LoadCertificateWithPrivateKey_auto.go deleted file mode 100644 index 376952b..0000000 --- a/sdk/device/LoadCertificateWithPrivateKey_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_LoadCertificateWithPrivateKey forwards the call to dev.CallMethod() then parses the payload of the reply as a LoadCertificateWithPrivateKeyResponse. -func Call_LoadCertificateWithPrivateKey(ctx context.Context, dev *onvif.Device, request device.LoadCertificateWithPrivateKey) (device.LoadCertificateWithPrivateKeyResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - LoadCertificateWithPrivateKeyResponse device.LoadCertificateWithPrivateKeyResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.LoadCertificateWithPrivateKeyResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "LoadCertificateWithPrivateKey") - return reply.Body.LoadCertificateWithPrivateKeyResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/LoadCertificates_auto.go b/sdk/device/LoadCertificates_auto.go deleted file mode 100644 index 7a2385a..0000000 --- a/sdk/device/LoadCertificates_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_LoadCertificates forwards the call to dev.CallMethod() then parses the payload of the reply as a LoadCertificatesResponse. -func Call_LoadCertificates(ctx context.Context, dev *onvif.Device, request device.LoadCertificates) (device.LoadCertificatesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - LoadCertificatesResponse device.LoadCertificatesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.LoadCertificatesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "LoadCertificates") - return reply.Body.LoadCertificatesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/RemoveIPAddressFilter_auto.go b/sdk/device/RemoveIPAddressFilter_auto.go deleted file mode 100644 index fabb4c6..0000000 --- a/sdk/device/RemoveIPAddressFilter_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_RemoveIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveIPAddressFilterResponse. -func Call_RemoveIPAddressFilter(ctx context.Context, dev *onvif.Device, request device.RemoveIPAddressFilter) (device.RemoveIPAddressFilterResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveIPAddressFilterResponse device.RemoveIPAddressFilterResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveIPAddressFilterResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveIPAddressFilter") - return reply.Body.RemoveIPAddressFilterResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/RemoveScopes_auto.go b/sdk/device/RemoveScopes_auto.go deleted file mode 100644 index 3aa699f..0000000 --- a/sdk/device/RemoveScopes_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_RemoveScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveScopesResponse. -func Call_RemoveScopes(ctx context.Context, dev *onvif.Device, request device.RemoveScopes) (device.RemoveScopesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveScopesResponse device.RemoveScopesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveScopesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveScopes") - return reply.Body.RemoveScopesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/RestoreSystem_auto.go b/sdk/device/RestoreSystem_auto.go deleted file mode 100644 index 4a18cc6..0000000 --- a/sdk/device/RestoreSystem_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_RestoreSystem forwards the call to dev.CallMethod() then parses the payload of the reply as a RestoreSystemResponse. -func Call_RestoreSystem(ctx context.Context, dev *onvif.Device, request device.RestoreSystem) (device.RestoreSystemResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RestoreSystemResponse device.RestoreSystemResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RestoreSystemResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RestoreSystem") - return reply.Body.RestoreSystemResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/ScanAvailableDot11Networks_auto.go b/sdk/device/ScanAvailableDot11Networks_auto.go deleted file mode 100644 index ba8a498..0000000 --- a/sdk/device/ScanAvailableDot11Networks_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_ScanAvailableDot11Networks forwards the call to dev.CallMethod() then parses the payload of the reply as a ScanAvailableDot11NetworksResponse. -func Call_ScanAvailableDot11Networks(ctx context.Context, dev *onvif.Device, request device.ScanAvailableDot11Networks) (device.ScanAvailableDot11NetworksResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - ScanAvailableDot11NetworksResponse device.ScanAvailableDot11NetworksResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.ScanAvailableDot11NetworksResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "ScanAvailableDot11Networks") - return reply.Body.ScanAvailableDot11NetworksResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SendAuxiliaryCommand_auto.go b/sdk/device/SendAuxiliaryCommand_auto.go deleted file mode 100644 index df23a07..0000000 --- a/sdk/device/SendAuxiliaryCommand_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SendAuxiliaryCommand forwards the call to dev.CallMethod() then parses the payload of the reply as a SendAuxiliaryCommandResponse. -func Call_SendAuxiliaryCommand(ctx context.Context, dev *onvif.Device, request device.SendAuxiliaryCommand) (device.SendAuxiliaryCommandResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SendAuxiliaryCommandResponse device.SendAuxiliaryCommandResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SendAuxiliaryCommandResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SendAuxiliaryCommand") - return reply.Body.SendAuxiliaryCommandResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetAccessPolicy_auto.go b/sdk/device/SetAccessPolicy_auto.go deleted file mode 100644 index 4c29b29..0000000 --- a/sdk/device/SetAccessPolicy_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetAccessPolicy forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAccessPolicyResponse. -func Call_SetAccessPolicy(ctx context.Context, dev *onvif.Device, request device.SetAccessPolicy) (device.SetAccessPolicyResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetAccessPolicyResponse device.SetAccessPolicyResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetAccessPolicyResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetAccessPolicy") - return reply.Body.SetAccessPolicyResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetCertificatesStatus_auto.go b/sdk/device/SetCertificatesStatus_auto.go deleted file mode 100644 index c3ff486..0000000 --- a/sdk/device/SetCertificatesStatus_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetCertificatesStatus forwards the call to dev.CallMethod() then parses the payload of the reply as a SetCertificatesStatusResponse. -func Call_SetCertificatesStatus(ctx context.Context, dev *onvif.Device, request device.SetCertificatesStatus) (device.SetCertificatesStatusResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetCertificatesStatusResponse device.SetCertificatesStatusResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetCertificatesStatusResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetCertificatesStatus") - return reply.Body.SetCertificatesStatusResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetClientCertificateMode_auto.go b/sdk/device/SetClientCertificateMode_auto.go deleted file mode 100644 index 3eae86f..0000000 --- a/sdk/device/SetClientCertificateMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetClientCertificateMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetClientCertificateModeResponse. -func Call_SetClientCertificateMode(ctx context.Context, dev *onvif.Device, request device.SetClientCertificateMode) (device.SetClientCertificateModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetClientCertificateModeResponse device.SetClientCertificateModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetClientCertificateModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetClientCertificateMode") - return reply.Body.SetClientCertificateModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetDNS_auto.go b/sdk/device/SetDNS_auto.go deleted file mode 100644 index 111f885..0000000 --- a/sdk/device/SetDNS_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDNSResponse. -func Call_SetDNS(ctx context.Context, dev *onvif.Device, request device.SetDNS) (device.SetDNSResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetDNSResponse device.SetDNSResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetDNSResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetDNS") - return reply.Body.SetDNSResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetDiscoveryMode_auto.go b/sdk/device/SetDiscoveryMode_auto.go deleted file mode 100644 index 605fcab..0000000 --- a/sdk/device/SetDiscoveryMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDiscoveryModeResponse. -func Call_SetDiscoveryMode(ctx context.Context, dev *onvif.Device, request device.SetDiscoveryMode) (device.SetDiscoveryModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetDiscoveryModeResponse device.SetDiscoveryModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetDiscoveryModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetDiscoveryMode") - return reply.Body.SetDiscoveryModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetDot1XConfiguration_auto.go b/sdk/device/SetDot1XConfiguration_auto.go deleted file mode 100644 index 9c54e53..0000000 --- a/sdk/device/SetDot1XConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetDot1XConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDot1XConfigurationResponse. -func Call_SetDot1XConfiguration(ctx context.Context, dev *onvif.Device, request device.SetDot1XConfiguration) (device.SetDot1XConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetDot1XConfigurationResponse device.SetDot1XConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetDot1XConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetDot1XConfiguration") - return reply.Body.SetDot1XConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetDynamicDNS_auto.go b/sdk/device/SetDynamicDNS_auto.go deleted file mode 100644 index 8eb6ee0..0000000 --- a/sdk/device/SetDynamicDNS_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetDynamicDNS forwards the call to dev.CallMethod() then parses the payload of the reply as a SetDynamicDNSResponse. -func Call_SetDynamicDNS(ctx context.Context, dev *onvif.Device, request device.SetDynamicDNS) (device.SetDynamicDNSResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetDynamicDNSResponse device.SetDynamicDNSResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetDynamicDNSResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetDynamicDNS") - return reply.Body.SetDynamicDNSResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetGeoLocation_auto.go b/sdk/device/SetGeoLocation_auto.go deleted file mode 100644 index 0e48399..0000000 --- a/sdk/device/SetGeoLocation_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetGeoLocation forwards the call to dev.CallMethod() then parses the payload of the reply as a SetGeoLocationResponse. -func Call_SetGeoLocation(ctx context.Context, dev *onvif.Device, request device.SetGeoLocation) (device.SetGeoLocationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetGeoLocationResponse device.SetGeoLocationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetGeoLocationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetGeoLocation") - return reply.Body.SetGeoLocationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetHostnameFromDHCP_auto.go b/sdk/device/SetHostnameFromDHCP_auto.go deleted file mode 100644 index 96c1af5..0000000 --- a/sdk/device/SetHostnameFromDHCP_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetHostnameFromDHCP forwards the call to dev.CallMethod() then parses the payload of the reply as a SetHostnameFromDHCPResponse. -func Call_SetHostnameFromDHCP(ctx context.Context, dev *onvif.Device, request device.SetHostnameFromDHCP) (device.SetHostnameFromDHCPResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetHostnameFromDHCPResponse device.SetHostnameFromDHCPResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetHostnameFromDHCPResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetHostnameFromDHCP") - return reply.Body.SetHostnameFromDHCPResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetHostname_auto.go b/sdk/device/SetHostname_auto.go deleted file mode 100644 index f7bae40..0000000 --- a/sdk/device/SetHostname_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetHostname forwards the call to dev.CallMethod() then parses the payload of the reply as a SetHostnameResponse. -func Call_SetHostname(ctx context.Context, dev *onvif.Device, request device.SetHostname) (device.SetHostnameResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetHostnameResponse device.SetHostnameResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetHostnameResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetHostname") - return reply.Body.SetHostnameResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetIPAddressFilter_auto.go b/sdk/device/SetIPAddressFilter_auto.go deleted file mode 100644 index 6900b6c..0000000 --- a/sdk/device/SetIPAddressFilter_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetIPAddressFilter forwards the call to dev.CallMethod() then parses the payload of the reply as a SetIPAddressFilterResponse. -func Call_SetIPAddressFilter(ctx context.Context, dev *onvif.Device, request device.SetIPAddressFilter) (device.SetIPAddressFilterResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetIPAddressFilterResponse device.SetIPAddressFilterResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetIPAddressFilterResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetIPAddressFilter") - return reply.Body.SetIPAddressFilterResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetNTP_auto.go b/sdk/device/SetNTP_auto.go deleted file mode 100644 index 92eeb0e..0000000 --- a/sdk/device/SetNTP_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetNTP forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNTPResponse. -func Call_SetNTP(ctx context.Context, dev *onvif.Device, request device.SetNTP) (device.SetNTPResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetNTPResponse device.SetNTPResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetNTPResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetNTP") - return reply.Body.SetNTPResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetNetworkDefaultGateway_auto.go b/sdk/device/SetNetworkDefaultGateway_auto.go deleted file mode 100644 index 8110bdd..0000000 --- a/sdk/device/SetNetworkDefaultGateway_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetNetworkDefaultGateway forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNetworkDefaultGatewayResponse. -func Call_SetNetworkDefaultGateway(ctx context.Context, dev *onvif.Device, request device.SetNetworkDefaultGateway) (device.SetNetworkDefaultGatewayResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetNetworkDefaultGatewayResponse device.SetNetworkDefaultGatewayResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetNetworkDefaultGatewayResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetNetworkDefaultGateway") - return reply.Body.SetNetworkDefaultGatewayResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetNetworkInterfaces_auto.go b/sdk/device/SetNetworkInterfaces_auto.go deleted file mode 100644 index 8e11245..0000000 --- a/sdk/device/SetNetworkInterfaces_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetNetworkInterfaces forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNetworkInterfacesResponse. -func Call_SetNetworkInterfaces(ctx context.Context, dev *onvif.Device, request device.SetNetworkInterfaces) (device.SetNetworkInterfacesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetNetworkInterfacesResponse device.SetNetworkInterfacesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetNetworkInterfacesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetNetworkInterfaces") - return reply.Body.SetNetworkInterfacesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetNetworkProtocols_auto.go b/sdk/device/SetNetworkProtocols_auto.go deleted file mode 100644 index 05cb82c..0000000 --- a/sdk/device/SetNetworkProtocols_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetNetworkProtocols forwards the call to dev.CallMethod() then parses the payload of the reply as a SetNetworkProtocolsResponse. -func Call_SetNetworkProtocols(ctx context.Context, dev *onvif.Device, request device.SetNetworkProtocols) (device.SetNetworkProtocolsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetNetworkProtocolsResponse device.SetNetworkProtocolsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetNetworkProtocolsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetNetworkProtocols") - return reply.Body.SetNetworkProtocolsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetRelayOutputSettings_auto.go b/sdk/device/SetRelayOutputSettings_auto.go deleted file mode 100644 index 5420134..0000000 --- a/sdk/device/SetRelayOutputSettings_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetRelayOutputSettings forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRelayOutputSettingsResponse. -func Call_SetRelayOutputSettings(ctx context.Context, dev *onvif.Device, request device.SetRelayOutputSettings) (device.SetRelayOutputSettingsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetRelayOutputSettingsResponse device.SetRelayOutputSettingsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetRelayOutputSettingsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetRelayOutputSettings") - return reply.Body.SetRelayOutputSettingsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetRelayOutputState_auto.go b/sdk/device/SetRelayOutputState_auto.go deleted file mode 100644 index f946c46..0000000 --- a/sdk/device/SetRelayOutputState_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetRelayOutputState forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRelayOutputStateResponse. -func Call_SetRelayOutputState(ctx context.Context, dev *onvif.Device, request device.SetRelayOutputState) (device.SetRelayOutputStateResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetRelayOutputStateResponse device.SetRelayOutputStateResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetRelayOutputStateResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetRelayOutputState") - return reply.Body.SetRelayOutputStateResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetRemoteDiscoveryMode_auto.go b/sdk/device/SetRemoteDiscoveryMode_auto.go deleted file mode 100644 index 53c143c..0000000 --- a/sdk/device/SetRemoteDiscoveryMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetRemoteDiscoveryMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRemoteDiscoveryModeResponse. -func Call_SetRemoteDiscoveryMode(ctx context.Context, dev *onvif.Device, request device.SetRemoteDiscoveryMode) (device.SetRemoteDiscoveryModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetRemoteDiscoveryModeResponse device.SetRemoteDiscoveryModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetRemoteDiscoveryModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetRemoteDiscoveryMode") - return reply.Body.SetRemoteDiscoveryModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetRemoteUser_auto.go b/sdk/device/SetRemoteUser_auto.go deleted file mode 100644 index 6d2454c..0000000 --- a/sdk/device/SetRemoteUser_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetRemoteUser forwards the call to dev.CallMethod() then parses the payload of the reply as a SetRemoteUserResponse. -func Call_SetRemoteUser(ctx context.Context, dev *onvif.Device, request device.SetRemoteUser) (device.SetRemoteUserResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetRemoteUserResponse device.SetRemoteUserResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetRemoteUserResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetRemoteUser") - return reply.Body.SetRemoteUserResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetScopes_auto.go b/sdk/device/SetScopes_auto.go deleted file mode 100644 index e1f0195..0000000 --- a/sdk/device/SetScopes_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetScopes forwards the call to dev.CallMethod() then parses the payload of the reply as a SetScopesResponse. -func Call_SetScopes(ctx context.Context, dev *onvif.Device, request device.SetScopes) (device.SetScopesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetScopesResponse device.SetScopesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetScopesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetScopes") - return reply.Body.SetScopesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetStorageConfiguration_auto.go b/sdk/device/SetStorageConfiguration_auto.go deleted file mode 100644 index 59dfcf1..0000000 --- a/sdk/device/SetStorageConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetStorageConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetStorageConfigurationResponse. -func Call_SetStorageConfiguration(ctx context.Context, dev *onvif.Device, request device.SetStorageConfiguration) (device.SetStorageConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetStorageConfigurationResponse device.SetStorageConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetStorageConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetStorageConfiguration") - return reply.Body.SetStorageConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetSystemDateAndTime_auto.go b/sdk/device/SetSystemDateAndTime_auto.go deleted file mode 100644 index 25f57bb..0000000 --- a/sdk/device/SetSystemDateAndTime_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetSystemDateAndTime forwards the call to dev.CallMethod() then parses the payload of the reply as a SetSystemDateAndTimeResponse. -func Call_SetSystemDateAndTime(ctx context.Context, dev *onvif.Device, request device.SetSystemDateAndTime) (device.SetSystemDateAndTimeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetSystemDateAndTimeResponse device.SetSystemDateAndTimeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetSystemDateAndTimeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetSystemDateAndTime") - return reply.Body.SetSystemDateAndTimeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetSystemFactoryDefault_auto.go b/sdk/device/SetSystemFactoryDefault_auto.go deleted file mode 100644 index 9616b10..0000000 --- a/sdk/device/SetSystemFactoryDefault_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetSystemFactoryDefault forwards the call to dev.CallMethod() then parses the payload of the reply as a SetSystemFactoryDefaultResponse. -func Call_SetSystemFactoryDefault(ctx context.Context, dev *onvif.Device, request device.SetSystemFactoryDefault) (device.SetSystemFactoryDefaultResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetSystemFactoryDefaultResponse device.SetSystemFactoryDefaultResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetSystemFactoryDefaultResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetSystemFactoryDefault") - return reply.Body.SetSystemFactoryDefaultResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetUser_auto.go b/sdk/device/SetUser_auto.go deleted file mode 100644 index 3c7f030..0000000 --- a/sdk/device/SetUser_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetUser forwards the call to dev.CallMethod() then parses the payload of the reply as a SetUserResponse. -func Call_SetUser(ctx context.Context, dev *onvif.Device, request device.SetUser) (device.SetUserResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetUserResponse device.SetUserResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetUserResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetUser") - return reply.Body.SetUserResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SetZeroConfiguration_auto.go b/sdk/device/SetZeroConfiguration_auto.go deleted file mode 100644 index a3b76e6..0000000 --- a/sdk/device/SetZeroConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SetZeroConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetZeroConfigurationResponse. -func Call_SetZeroConfiguration(ctx context.Context, dev *onvif.Device, request device.SetZeroConfiguration) (device.SetZeroConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetZeroConfigurationResponse device.SetZeroConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetZeroConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetZeroConfiguration") - return reply.Body.SetZeroConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/StartFirmwareUpgrade_auto.go b/sdk/device/StartFirmwareUpgrade_auto.go deleted file mode 100644 index e890ae2..0000000 --- a/sdk/device/StartFirmwareUpgrade_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_StartFirmwareUpgrade forwards the call to dev.CallMethod() then parses the payload of the reply as a StartFirmwareUpgradeResponse. -func Call_StartFirmwareUpgrade(ctx context.Context, dev *onvif.Device, request device.StartFirmwareUpgrade) (device.StartFirmwareUpgradeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - StartFirmwareUpgradeResponse device.StartFirmwareUpgradeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.StartFirmwareUpgradeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "StartFirmwareUpgrade") - return reply.Body.StartFirmwareUpgradeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/StartSystemRestore_auto.go b/sdk/device/StartSystemRestore_auto.go deleted file mode 100644 index ab9d35d..0000000 --- a/sdk/device/StartSystemRestore_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_StartSystemRestore forwards the call to dev.CallMethod() then parses the payload of the reply as a StartSystemRestoreResponse. -func Call_StartSystemRestore(ctx context.Context, dev *onvif.Device, request device.StartSystemRestore) (device.StartSystemRestoreResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - StartSystemRestoreResponse device.StartSystemRestoreResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.StartSystemRestoreResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "StartSystemRestore") - return reply.Body.StartSystemRestoreResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/SystemReboot_auto.go b/sdk/device/SystemReboot_auto.go deleted file mode 100644 index 6ad6831..0000000 --- a/sdk/device/SystemReboot_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_SystemReboot forwards the call to dev.CallMethod() then parses the payload of the reply as a SystemRebootResponse. -func Call_SystemReboot(ctx context.Context, dev *onvif.Device, request device.SystemReboot) (device.SystemRebootResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SystemRebootResponse device.SystemRebootResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SystemRebootResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SystemReboot") - return reply.Body.SystemRebootResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/UpgradeSystemFirmware_auto.go b/sdk/device/UpgradeSystemFirmware_auto.go deleted file mode 100644 index a6d63af..0000000 --- a/sdk/device/UpgradeSystemFirmware_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package device - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/device" -) - -// Call_UpgradeSystemFirmware forwards the call to dev.CallMethod() then parses the payload of the reply as a UpgradeSystemFirmwareResponse. -func Call_UpgradeSystemFirmware(ctx context.Context, dev *onvif.Device, request device.UpgradeSystemFirmware) (device.UpgradeSystemFirmwareResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - UpgradeSystemFirmwareResponse device.UpgradeSystemFirmwareResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.UpgradeSystemFirmwareResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "UpgradeSystemFirmware") - return reply.Body.UpgradeSystemFirmwareResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/device/device.go b/sdk/device/device.go deleted file mode 100644 index 1f4c1a8..0000000 --- a/sdk/device/device.go +++ /dev/null @@ -1,91 +0,0 @@ -package device - -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetServices -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetServiceCapabilities -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDeviceInformation -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetSystemDateAndTime -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemDateAndTime -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetSystemFactoryDefault -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device UpgradeSystemFirmware -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SystemReboot -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device RestoreSystem -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemBackup -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemLog -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemSupportInformation -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetScopes -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetScopes -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device AddScopes -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device RemoveScopes -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDiscoveryMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDiscoveryMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetRemoteDiscoveryMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRemoteDiscoveryMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDPAddresses -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetEndpointReference -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetRemoteUser -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRemoteUser -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetUsers -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateUsers -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteUsers -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetUser -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetWsdlUrl -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCapabilities -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetHostname -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetHostname -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetHostnameFromDHCP -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDNS -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDNS -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNTP -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNTP -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDynamicDNS -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDynamicDNS -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNetworkInterfaces -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNetworkInterfaces -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNetworkProtocols -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNetworkProtocols -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetNetworkDefaultGateway -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetNetworkDefaultGateway -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetZeroConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetZeroConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetIPAddressFilter -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetIPAddressFilter -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device AddIPAddressFilter -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device RemoveIPAddressFilter -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetAccessPolicy -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetAccessPolicy -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateCertificate -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCertificates -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCertificatesStatus -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetCertificatesStatus -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteCertificates -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetPkcs10Request -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device LoadCertificates -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetClientCertificateMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetClientCertificateMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetRelayOutputs -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRelayOutputSettings -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetRelayOutputState -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SendAuxiliaryCommand -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCACertificates -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device LoadCertificateWithPrivateKey -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetCertificateInformation -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device LoadCACertificates -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateDot1XConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetDot1XConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot1XConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot1XConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteDot1XConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot11Capabilities -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetDot11Status -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device ScanAvailableDot11Networks -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetSystemUris -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device StartFirmwareUpgrade -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device StartSystemRestore -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetStorageConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device CreateStorageConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetStorageConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetStorageConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteStorageConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device GetGeoLocation -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device SetGeoLocation -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen device device DeleteGeoLocation diff --git a/sdk/event/CreatePullPointSubscription_auto.go b/sdk/event/CreatePullPointSubscription_auto.go deleted file mode 100644 index 971f31b..0000000 --- a/sdk/event/CreatePullPointSubscription_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package event - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/event" -) - -// Call_CreatePullPointSubscription forwards the call to dev.CallMethod() then parses the payload of the reply as a CreatePullPointSubscriptionResponse. -func Call_CreatePullPointSubscription(ctx context.Context, dev *onvif.Device, request event.CreatePullPointSubscription) (event.CreatePullPointSubscriptionResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreatePullPointSubscriptionResponse event.CreatePullPointSubscriptionResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreatePullPointSubscriptionResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreatePullPointSubscription") - return reply.Body.CreatePullPointSubscriptionResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/event/GetEventProperties_auto.go b/sdk/event/GetEventProperties_auto.go deleted file mode 100644 index 839ad5d..0000000 --- a/sdk/event/GetEventProperties_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package event - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/event" -) - -// Call_GetEventProperties forwards the call to dev.CallMethod() then parses the payload of the reply as a GetEventPropertiesResponse. -func Call_GetEventProperties(ctx context.Context, dev *onvif.Device, request event.GetEventProperties) (event.GetEventPropertiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetEventPropertiesResponse event.GetEventPropertiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetEventPropertiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetEventProperties") - return reply.Body.GetEventPropertiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/event/GetServiceCapabilities_auto.go b/sdk/event/GetServiceCapabilities_auto.go deleted file mode 100644 index df48a71..0000000 --- a/sdk/event/GetServiceCapabilities_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package event - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/event" -) - -// Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. -func Call_GetServiceCapabilities(ctx context.Context, dev *onvif.Device, request event.GetServiceCapabilities) (event.GetServiceCapabilitiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetServiceCapabilitiesResponse event.GetServiceCapabilitiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetServiceCapabilities") - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/event/Subscribe_auto.go b/sdk/event/Subscribe_auto.go deleted file mode 100644 index 6c41131..0000000 --- a/sdk/event/Subscribe_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package event - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/event" -) - -// Call_Subscribe forwards the call to dev.CallMethod() then parses the payload of the reply as a SubscribeResponse. -func Call_Subscribe(ctx context.Context, dev *onvif.Device, request event.Subscribe) (event.SubscribeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SubscribeResponse event.SubscribeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SubscribeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "Subscribe") - return reply.Body.SubscribeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/event/Unsubscribe_auto.go b/sdk/event/Unsubscribe_auto.go deleted file mode 100644 index 0faa9ea..0000000 --- a/sdk/event/Unsubscribe_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package event - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/event" -) - -// Call_Unsubscribe forwards the call to dev.CallMethod() then parses the payload of the reply as a UnsubscribeResponse. -func Call_Unsubscribe(ctx context.Context, dev *onvif.Device, request event.Unsubscribe) (event.UnsubscribeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - UnsubscribeResponse event.UnsubscribeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.UnsubscribeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "Unsubscribe") - return reply.Body.UnsubscribeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddAudioDecoderConfiguration_auto.go b/sdk/media/AddAudioDecoderConfiguration_auto.go deleted file mode 100644 index f262669..0000000 --- a/sdk/media/AddAudioDecoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioDecoderConfigurationResponse. -func Call_AddAudioDecoderConfiguration(ctx context.Context, dev *onvif.Device, request media.AddAudioDecoderConfiguration) (media.AddAudioDecoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddAudioDecoderConfigurationResponse media.AddAudioDecoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddAudioDecoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddAudioDecoderConfiguration") - return reply.Body.AddAudioDecoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddAudioEncoderConfiguration_auto.go b/sdk/media/AddAudioEncoderConfiguration_auto.go deleted file mode 100644 index 5e616a3..0000000 --- a/sdk/media/AddAudioEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioEncoderConfigurationResponse. -func Call_AddAudioEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.AddAudioEncoderConfiguration) (media.AddAudioEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddAudioEncoderConfigurationResponse media.AddAudioEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddAudioEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddAudioEncoderConfiguration") - return reply.Body.AddAudioEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddAudioOutputConfiguration_auto.go b/sdk/media/AddAudioOutputConfiguration_auto.go deleted file mode 100644 index 8de8e86..0000000 --- a/sdk/media/AddAudioOutputConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioOutputConfigurationResponse. -func Call_AddAudioOutputConfiguration(ctx context.Context, dev *onvif.Device, request media.AddAudioOutputConfiguration) (media.AddAudioOutputConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddAudioOutputConfigurationResponse media.AddAudioOutputConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddAudioOutputConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddAudioOutputConfiguration") - return reply.Body.AddAudioOutputConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddAudioSourceConfiguration_auto.go b/sdk/media/AddAudioSourceConfiguration_auto.go deleted file mode 100644 index 2b53799..0000000 --- a/sdk/media/AddAudioSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddAudioSourceConfigurationResponse. -func Call_AddAudioSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.AddAudioSourceConfiguration) (media.AddAudioSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddAudioSourceConfigurationResponse media.AddAudioSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddAudioSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddAudioSourceConfiguration") - return reply.Body.AddAudioSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddMetadataConfiguration_auto.go b/sdk/media/AddMetadataConfiguration_auto.go deleted file mode 100644 index e2139a7..0000000 --- a/sdk/media/AddMetadataConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddMetadataConfigurationResponse. -func Call_AddMetadataConfiguration(ctx context.Context, dev *onvif.Device, request media.AddMetadataConfiguration) (media.AddMetadataConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddMetadataConfigurationResponse media.AddMetadataConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddMetadataConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddMetadataConfiguration") - return reply.Body.AddMetadataConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddPTZConfiguration_auto.go b/sdk/media/AddPTZConfiguration_auto.go deleted file mode 100644 index e3ed43d..0000000 --- a/sdk/media/AddPTZConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddPTZConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddPTZConfigurationResponse. -func Call_AddPTZConfiguration(ctx context.Context, dev *onvif.Device, request media.AddPTZConfiguration) (media.AddPTZConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddPTZConfigurationResponse media.AddPTZConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddPTZConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddPTZConfiguration") - return reply.Body.AddPTZConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddVideoAnalyticsConfiguration_auto.go b/sdk/media/AddVideoAnalyticsConfiguration_auto.go deleted file mode 100644 index c0c66da..0000000 --- a/sdk/media/AddVideoAnalyticsConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddVideoAnalyticsConfigurationResponse. -func Call_AddVideoAnalyticsConfiguration(ctx context.Context, dev *onvif.Device, request media.AddVideoAnalyticsConfiguration) (media.AddVideoAnalyticsConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddVideoAnalyticsConfigurationResponse media.AddVideoAnalyticsConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddVideoAnalyticsConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddVideoAnalyticsConfiguration") - return reply.Body.AddVideoAnalyticsConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddVideoEncoderConfiguration_auto.go b/sdk/media/AddVideoEncoderConfiguration_auto.go deleted file mode 100644 index c5b5764..0000000 --- a/sdk/media/AddVideoEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddVideoEncoderConfigurationResponse. -func Call_AddVideoEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.AddVideoEncoderConfiguration) (media.AddVideoEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddVideoEncoderConfigurationResponse media.AddVideoEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddVideoEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddVideoEncoderConfiguration") - return reply.Body.AddVideoEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/AddVideoSourceConfiguration_auto.go b/sdk/media/AddVideoSourceConfiguration_auto.go deleted file mode 100644 index 8f67d80..0000000 --- a/sdk/media/AddVideoSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_AddVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a AddVideoSourceConfigurationResponse. -func Call_AddVideoSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.AddVideoSourceConfiguration) (media.AddVideoSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AddVideoSourceConfigurationResponse media.AddVideoSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AddVideoSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AddVideoSourceConfiguration") - return reply.Body.AddVideoSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/CreateOSD_auto.go b/sdk/media/CreateOSD_auto.go deleted file mode 100644 index 8fa9641..0000000 --- a/sdk/media/CreateOSD_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_CreateOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateOSDResponse. -func Call_CreateOSD(ctx context.Context, dev *onvif.Device, request media.CreateOSD) (media.CreateOSDResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreateOSDResponse media.CreateOSDResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreateOSDResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreateOSD") - return reply.Body.CreateOSDResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/CreateProfile_auto.go b/sdk/media/CreateProfile_auto.go deleted file mode 100644 index 401e913..0000000 --- a/sdk/media/CreateProfile_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_CreateProfile forwards the call to dev.CallMethod() then parses the payload of the reply as a CreateProfileResponse. -func Call_CreateProfile(ctx context.Context, dev *onvif.Device, request media.CreateProfile) (media.CreateProfileResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreateProfileResponse media.CreateProfileResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreateProfileResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreateProfile") - return reply.Body.CreateProfileResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/DeleteOSD_auto.go b/sdk/media/DeleteOSD_auto.go deleted file mode 100644 index 896604c..0000000 --- a/sdk/media/DeleteOSD_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_DeleteOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteOSDResponse. -func Call_DeleteOSD(ctx context.Context, dev *onvif.Device, request media.DeleteOSD) (media.DeleteOSDResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteOSDResponse media.DeleteOSDResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteOSDResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteOSD") - return reply.Body.DeleteOSDResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/DeleteProfile_auto.go b/sdk/media/DeleteProfile_auto.go deleted file mode 100644 index 16e1564..0000000 --- a/sdk/media/DeleteProfile_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_DeleteProfile forwards the call to dev.CallMethod() then parses the payload of the reply as a DeleteProfileResponse. -func Call_DeleteProfile(ctx context.Context, dev *onvif.Device, request media.DeleteProfile) (media.DeleteProfileResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - DeleteProfileResponse media.DeleteProfileResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.DeleteProfileResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "DeleteProfile") - return reply.Body.DeleteProfileResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioDecoderConfigurationOptions_auto.go b/sdk/media/GetAudioDecoderConfigurationOptions_auto.go deleted file mode 100644 index e396d37..0000000 --- a/sdk/media/GetAudioDecoderConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioDecoderConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioDecoderConfigurationOptionsResponse. -func Call_GetAudioDecoderConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetAudioDecoderConfigurationOptions) (media.GetAudioDecoderConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioDecoderConfigurationOptionsResponse media.GetAudioDecoderConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioDecoderConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioDecoderConfigurationOptions") - return reply.Body.GetAudioDecoderConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioDecoderConfiguration_auto.go b/sdk/media/GetAudioDecoderConfiguration_auto.go deleted file mode 100644 index 0879aaa..0000000 --- a/sdk/media/GetAudioDecoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioDecoderConfigurationResponse. -func Call_GetAudioDecoderConfiguration(ctx context.Context, dev *onvif.Device, request media.GetAudioDecoderConfiguration) (media.GetAudioDecoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioDecoderConfigurationResponse media.GetAudioDecoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioDecoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioDecoderConfiguration") - return reply.Body.GetAudioDecoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioDecoderConfigurations_auto.go b/sdk/media/GetAudioDecoderConfigurations_auto.go deleted file mode 100644 index 899aef9..0000000 --- a/sdk/media/GetAudioDecoderConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioDecoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioDecoderConfigurationsResponse. -func Call_GetAudioDecoderConfigurations(ctx context.Context, dev *onvif.Device, request media.GetAudioDecoderConfigurations) (media.GetAudioDecoderConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioDecoderConfigurationsResponse media.GetAudioDecoderConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioDecoderConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioDecoderConfigurations") - return reply.Body.GetAudioDecoderConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioEncoderConfigurationOptions_auto.go b/sdk/media/GetAudioEncoderConfigurationOptions_auto.go deleted file mode 100644 index 5b7e718..0000000 --- a/sdk/media/GetAudioEncoderConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioEncoderConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioEncoderConfigurationOptionsResponse. -func Call_GetAudioEncoderConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetAudioEncoderConfigurationOptions) (media.GetAudioEncoderConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioEncoderConfigurationOptionsResponse media.GetAudioEncoderConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioEncoderConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioEncoderConfigurationOptions") - return reply.Body.GetAudioEncoderConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioEncoderConfiguration_auto.go b/sdk/media/GetAudioEncoderConfiguration_auto.go deleted file mode 100644 index 7dd8029..0000000 --- a/sdk/media/GetAudioEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioEncoderConfigurationResponse. -func Call_GetAudioEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.GetAudioEncoderConfiguration) (media.GetAudioEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioEncoderConfigurationResponse media.GetAudioEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioEncoderConfiguration") - return reply.Body.GetAudioEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioEncoderConfigurations_auto.go b/sdk/media/GetAudioEncoderConfigurations_auto.go deleted file mode 100644 index 62cea4b..0000000 --- a/sdk/media/GetAudioEncoderConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioEncoderConfigurationsResponse. -func Call_GetAudioEncoderConfigurations(ctx context.Context, dev *onvif.Device, request media.GetAudioEncoderConfigurations) (media.GetAudioEncoderConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioEncoderConfigurationsResponse media.GetAudioEncoderConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioEncoderConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioEncoderConfigurations") - return reply.Body.GetAudioEncoderConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioOutputConfigurationOptions_auto.go b/sdk/media/GetAudioOutputConfigurationOptions_auto.go deleted file mode 100644 index db0f4a1..0000000 --- a/sdk/media/GetAudioOutputConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioOutputConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputConfigurationOptionsResponse. -func Call_GetAudioOutputConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetAudioOutputConfigurationOptions) (media.GetAudioOutputConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioOutputConfigurationOptionsResponse media.GetAudioOutputConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioOutputConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioOutputConfigurationOptions") - return reply.Body.GetAudioOutputConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioOutputConfiguration_auto.go b/sdk/media/GetAudioOutputConfiguration_auto.go deleted file mode 100644 index 9292b8b..0000000 --- a/sdk/media/GetAudioOutputConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputConfigurationResponse. -func Call_GetAudioOutputConfiguration(ctx context.Context, dev *onvif.Device, request media.GetAudioOutputConfiguration) (media.GetAudioOutputConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioOutputConfigurationResponse media.GetAudioOutputConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioOutputConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioOutputConfiguration") - return reply.Body.GetAudioOutputConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioOutputConfigurations_auto.go b/sdk/media/GetAudioOutputConfigurations_auto.go deleted file mode 100644 index be284cd..0000000 --- a/sdk/media/GetAudioOutputConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioOutputConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputConfigurationsResponse. -func Call_GetAudioOutputConfigurations(ctx context.Context, dev *onvif.Device, request media.GetAudioOutputConfigurations) (media.GetAudioOutputConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioOutputConfigurationsResponse media.GetAudioOutputConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioOutputConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioOutputConfigurations") - return reply.Body.GetAudioOutputConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioOutputs_auto.go b/sdk/media/GetAudioOutputs_auto.go deleted file mode 100644 index 50cc9b3..0000000 --- a/sdk/media/GetAudioOutputs_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioOutputs forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioOutputsResponse. -func Call_GetAudioOutputs(ctx context.Context, dev *onvif.Device, request media.GetAudioOutputs) (media.GetAudioOutputsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioOutputsResponse media.GetAudioOutputsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioOutputsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioOutputs") - return reply.Body.GetAudioOutputsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioSourceConfigurationOptions_auto.go b/sdk/media/GetAudioSourceConfigurationOptions_auto.go deleted file mode 100644 index a9584ea..0000000 --- a/sdk/media/GetAudioSourceConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioSourceConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourceConfigurationOptionsResponse. -func Call_GetAudioSourceConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetAudioSourceConfigurationOptions) (media.GetAudioSourceConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioSourceConfigurationOptionsResponse media.GetAudioSourceConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioSourceConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioSourceConfigurationOptions") - return reply.Body.GetAudioSourceConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioSourceConfiguration_auto.go b/sdk/media/GetAudioSourceConfiguration_auto.go deleted file mode 100644 index 8d287e8..0000000 --- a/sdk/media/GetAudioSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourceConfigurationResponse. -func Call_GetAudioSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.GetAudioSourceConfiguration) (media.GetAudioSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioSourceConfigurationResponse media.GetAudioSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioSourceConfiguration") - return reply.Body.GetAudioSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioSourceConfigurations_auto.go b/sdk/media/GetAudioSourceConfigurations_auto.go deleted file mode 100644 index d29ca2e..0000000 --- a/sdk/media/GetAudioSourceConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourceConfigurationsResponse. -func Call_GetAudioSourceConfigurations(ctx context.Context, dev *onvif.Device, request media.GetAudioSourceConfigurations) (media.GetAudioSourceConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioSourceConfigurationsResponse media.GetAudioSourceConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioSourceConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioSourceConfigurations") - return reply.Body.GetAudioSourceConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetAudioSources_auto.go b/sdk/media/GetAudioSources_auto.go deleted file mode 100644 index 833a54f..0000000 --- a/sdk/media/GetAudioSources_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetAudioSources forwards the call to dev.CallMethod() then parses the payload of the reply as a GetAudioSourcesResponse. -func Call_GetAudioSources(ctx context.Context, dev *onvif.Device, request media.GetAudioSources) (media.GetAudioSourcesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetAudioSourcesResponse media.GetAudioSourcesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetAudioSourcesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetAudioSources") - return reply.Body.GetAudioSourcesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go b/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go deleted file mode 100644 index 1d2e171..0000000 --- a/sdk/media/GetCompatibleAudioDecoderConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleAudioDecoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioDecoderConfigurationsResponse. -func Call_GetCompatibleAudioDecoderConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleAudioDecoderConfigurations) (media.GetCompatibleAudioDecoderConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleAudioDecoderConfigurationsResponse media.GetCompatibleAudioDecoderConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleAudioDecoderConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleAudioDecoderConfigurations") - return reply.Body.GetCompatibleAudioDecoderConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go b/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go deleted file mode 100644 index dcf1c3c..0000000 --- a/sdk/media/GetCompatibleAudioEncoderConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleAudioEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioEncoderConfigurationsResponse. -func Call_GetCompatibleAudioEncoderConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleAudioEncoderConfigurations) (media.GetCompatibleAudioEncoderConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleAudioEncoderConfigurationsResponse media.GetCompatibleAudioEncoderConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleAudioEncoderConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleAudioEncoderConfigurations") - return reply.Body.GetCompatibleAudioEncoderConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go b/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go deleted file mode 100644 index d88cb06..0000000 --- a/sdk/media/GetCompatibleAudioOutputConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleAudioOutputConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioOutputConfigurationsResponse. -func Call_GetCompatibleAudioOutputConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleAudioOutputConfigurations) (media.GetCompatibleAudioOutputConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleAudioOutputConfigurationsResponse media.GetCompatibleAudioOutputConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleAudioOutputConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleAudioOutputConfigurations") - return reply.Body.GetCompatibleAudioOutputConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go b/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go deleted file mode 100644 index b40e395..0000000 --- a/sdk/media/GetCompatibleAudioSourceConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleAudioSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleAudioSourceConfigurationsResponse. -func Call_GetCompatibleAudioSourceConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleAudioSourceConfigurations) (media.GetCompatibleAudioSourceConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleAudioSourceConfigurationsResponse media.GetCompatibleAudioSourceConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleAudioSourceConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleAudioSourceConfigurations") - return reply.Body.GetCompatibleAudioSourceConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleMetadataConfigurations_auto.go b/sdk/media/GetCompatibleMetadataConfigurations_auto.go deleted file mode 100644 index 6791715..0000000 --- a/sdk/media/GetCompatibleMetadataConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleMetadataConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleMetadataConfigurationsResponse. -func Call_GetCompatibleMetadataConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleMetadataConfigurations) (media.GetCompatibleMetadataConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleMetadataConfigurationsResponse media.GetCompatibleMetadataConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleMetadataConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleMetadataConfigurations") - return reply.Body.GetCompatibleMetadataConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go b/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go deleted file mode 100644 index 4ef36ab..0000000 --- a/sdk/media/GetCompatibleVideoAnalyticsConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleVideoAnalyticsConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleVideoAnalyticsConfigurationsResponse. -func Call_GetCompatibleVideoAnalyticsConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleVideoAnalyticsConfigurations) (media.GetCompatibleVideoAnalyticsConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleVideoAnalyticsConfigurationsResponse media.GetCompatibleVideoAnalyticsConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleVideoAnalyticsConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleVideoAnalyticsConfigurations") - return reply.Body.GetCompatibleVideoAnalyticsConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go b/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go deleted file mode 100644 index 2ddf7a8..0000000 --- a/sdk/media/GetCompatibleVideoEncoderConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleVideoEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleVideoEncoderConfigurationsResponse. -func Call_GetCompatibleVideoEncoderConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleVideoEncoderConfigurations) (media.GetCompatibleVideoEncoderConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleVideoEncoderConfigurationsResponse media.GetCompatibleVideoEncoderConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleVideoEncoderConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleVideoEncoderConfigurations") - return reply.Body.GetCompatibleVideoEncoderConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go b/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go deleted file mode 100644 index 80d3133..0000000 --- a/sdk/media/GetCompatibleVideoSourceConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetCompatibleVideoSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleVideoSourceConfigurationsResponse. -func Call_GetCompatibleVideoSourceConfigurations(ctx context.Context, dev *onvif.Device, request media.GetCompatibleVideoSourceConfigurations) (media.GetCompatibleVideoSourceConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleVideoSourceConfigurationsResponse media.GetCompatibleVideoSourceConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleVideoSourceConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleVideoSourceConfigurations") - return reply.Body.GetCompatibleVideoSourceConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go b/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go deleted file mode 100644 index b3535d8..0000000 --- a/sdk/media/GetGuaranteedNumberOfVideoEncoderInstances_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetGuaranteedNumberOfVideoEncoderInstances forwards the call to dev.CallMethod() then parses the payload of the reply as a GetGuaranteedNumberOfVideoEncoderInstancesResponse. -func Call_GetGuaranteedNumberOfVideoEncoderInstances(ctx context.Context, dev *onvif.Device, request media.GetGuaranteedNumberOfVideoEncoderInstances) (media.GetGuaranteedNumberOfVideoEncoderInstancesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetGuaranteedNumberOfVideoEncoderInstancesResponse media.GetGuaranteedNumberOfVideoEncoderInstancesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetGuaranteedNumberOfVideoEncoderInstancesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetGuaranteedNumberOfVideoEncoderInstances") - return reply.Body.GetGuaranteedNumberOfVideoEncoderInstancesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetMetadataConfigurationOptions_auto.go b/sdk/media/GetMetadataConfigurationOptions_auto.go deleted file mode 100644 index ad67d6b..0000000 --- a/sdk/media/GetMetadataConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetMetadataConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetMetadataConfigurationOptionsResponse. -func Call_GetMetadataConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetMetadataConfigurationOptions) (media.GetMetadataConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetMetadataConfigurationOptionsResponse media.GetMetadataConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetMetadataConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetMetadataConfigurationOptions") - return reply.Body.GetMetadataConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetMetadataConfiguration_auto.go b/sdk/media/GetMetadataConfiguration_auto.go deleted file mode 100644 index 94af08b..0000000 --- a/sdk/media/GetMetadataConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetMetadataConfigurationResponse. -func Call_GetMetadataConfiguration(ctx context.Context, dev *onvif.Device, request media.GetMetadataConfiguration) (media.GetMetadataConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetMetadataConfigurationResponse media.GetMetadataConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetMetadataConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetMetadataConfiguration") - return reply.Body.GetMetadataConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetMetadataConfigurations_auto.go b/sdk/media/GetMetadataConfigurations_auto.go deleted file mode 100644 index 13d3141..0000000 --- a/sdk/media/GetMetadataConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetMetadataConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetMetadataConfigurationsResponse. -func Call_GetMetadataConfigurations(ctx context.Context, dev *onvif.Device, request media.GetMetadataConfigurations) (media.GetMetadataConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetMetadataConfigurationsResponse media.GetMetadataConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetMetadataConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetMetadataConfigurations") - return reply.Body.GetMetadataConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetOSDOptions_auto.go b/sdk/media/GetOSDOptions_auto.go deleted file mode 100644 index d92d20a..0000000 --- a/sdk/media/GetOSDOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetOSDOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetOSDOptionsResponse. -func Call_GetOSDOptions(ctx context.Context, dev *onvif.Device, request media.GetOSDOptions) (media.GetOSDOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetOSDOptionsResponse media.GetOSDOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetOSDOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetOSDOptions") - return reply.Body.GetOSDOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetOSD_auto.go b/sdk/media/GetOSD_auto.go deleted file mode 100644 index 6a5483d..0000000 --- a/sdk/media/GetOSD_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a GetOSDResponse. -func Call_GetOSD(ctx context.Context, dev *onvif.Device, request media.GetOSD) (media.GetOSDResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetOSDResponse media.GetOSDResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetOSDResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetOSD") - return reply.Body.GetOSDResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetOSDs_auto.go b/sdk/media/GetOSDs_auto.go deleted file mode 100644 index f6a713c..0000000 --- a/sdk/media/GetOSDs_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetOSDs forwards the call to dev.CallMethod() then parses the payload of the reply as a GetOSDsResponse. -func Call_GetOSDs(ctx context.Context, dev *onvif.Device, request media.GetOSDs) (media.GetOSDsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetOSDsResponse media.GetOSDsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetOSDsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetOSDs") - return reply.Body.GetOSDsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetProfile_auto.go b/sdk/media/GetProfile_auto.go deleted file mode 100644 index d43b9cb..0000000 --- a/sdk/media/GetProfile_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetProfile forwards the call to dev.CallMethod() then parses the payload of the reply as a GetProfileResponse. -func Call_GetProfile(ctx context.Context, dev *onvif.Device, request media.GetProfile) (media.GetProfileResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetProfileResponse media.GetProfileResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetProfileResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetProfile") - return reply.Body.GetProfileResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetProfiles_auto.go b/sdk/media/GetProfiles_auto.go deleted file mode 100644 index b081b46..0000000 --- a/sdk/media/GetProfiles_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetProfiles forwards the call to dev.CallMethod() then parses the payload of the reply as a GetProfilesResponse. -func Call_GetProfiles(ctx context.Context, dev *onvif.Device, request media.GetProfiles) (media.GetProfilesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetProfilesResponse media.GetProfilesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetProfilesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetProfiles") - return reply.Body.GetProfilesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetServiceCapabilities_auto.go b/sdk/media/GetServiceCapabilities_auto.go deleted file mode 100644 index 9459292..0000000 --- a/sdk/media/GetServiceCapabilities_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. -func Call_GetServiceCapabilities(ctx context.Context, dev *onvif.Device, request media.GetServiceCapabilities) (media.GetServiceCapabilitiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetServiceCapabilitiesResponse media.GetServiceCapabilitiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetServiceCapabilities") - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetSnapshotUri_auto.go b/sdk/media/GetSnapshotUri_auto.go deleted file mode 100644 index d325d37..0000000 --- a/sdk/media/GetSnapshotUri_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetSnapshotUri forwards the call to dev.CallMethod() then parses the payload of the reply as a GetSnapshotUriResponse. -func Call_GetSnapshotUri(ctx context.Context, dev *onvif.Device, request media.GetSnapshotUri) (media.GetSnapshotUriResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetSnapshotUriResponse media.GetSnapshotUriResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetSnapshotUriResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetSnapshotUri") - return reply.Body.GetSnapshotUriResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetStreamUri_auto.go b/sdk/media/GetStreamUri_auto.go deleted file mode 100644 index ebb345b..0000000 --- a/sdk/media/GetStreamUri_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetStreamUri forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStreamUriResponse. -func Call_GetStreamUri(ctx context.Context, dev *onvif.Device, request media.GetStreamUri) (media.GetStreamUriResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetStreamUriResponse media.GetStreamUriResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetStreamUriResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetStreamUri") - return reply.Body.GetStreamUriResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoAnalyticsConfiguration_auto.go b/sdk/media/GetVideoAnalyticsConfiguration_auto.go deleted file mode 100644 index bbeb7d3..0000000 --- a/sdk/media/GetVideoAnalyticsConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoAnalyticsConfigurationResponse. -func Call_GetVideoAnalyticsConfiguration(ctx context.Context, dev *onvif.Device, request media.GetVideoAnalyticsConfiguration) (media.GetVideoAnalyticsConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoAnalyticsConfigurationResponse media.GetVideoAnalyticsConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoAnalyticsConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoAnalyticsConfiguration") - return reply.Body.GetVideoAnalyticsConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoAnalyticsConfigurations_auto.go b/sdk/media/GetVideoAnalyticsConfigurations_auto.go deleted file mode 100644 index a2071aa..0000000 --- a/sdk/media/GetVideoAnalyticsConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoAnalyticsConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoAnalyticsConfigurationsResponse. -func Call_GetVideoAnalyticsConfigurations(ctx context.Context, dev *onvif.Device, request media.GetVideoAnalyticsConfigurations) (media.GetVideoAnalyticsConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoAnalyticsConfigurationsResponse media.GetVideoAnalyticsConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoAnalyticsConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoAnalyticsConfigurations") - return reply.Body.GetVideoAnalyticsConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoEncoderConfigurationOptions_auto.go b/sdk/media/GetVideoEncoderConfigurationOptions_auto.go deleted file mode 100644 index 9380e74..0000000 --- a/sdk/media/GetVideoEncoderConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoEncoderConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoEncoderConfigurationOptionsResponse. -func Call_GetVideoEncoderConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetVideoEncoderConfigurationOptions) (media.GetVideoEncoderConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoEncoderConfigurationOptionsResponse media.GetVideoEncoderConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoEncoderConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoEncoderConfigurationOptions") - return reply.Body.GetVideoEncoderConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoEncoderConfiguration_auto.go b/sdk/media/GetVideoEncoderConfiguration_auto.go deleted file mode 100644 index d5aece0..0000000 --- a/sdk/media/GetVideoEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoEncoderConfigurationResponse. -func Call_GetVideoEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.GetVideoEncoderConfiguration) (media.GetVideoEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoEncoderConfigurationResponse media.GetVideoEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoEncoderConfiguration") - return reply.Body.GetVideoEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoEncoderConfigurations_auto.go b/sdk/media/GetVideoEncoderConfigurations_auto.go deleted file mode 100644 index 8932c92..0000000 --- a/sdk/media/GetVideoEncoderConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoEncoderConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoEncoderConfigurationsResponse. -func Call_GetVideoEncoderConfigurations(ctx context.Context, dev *onvif.Device, request media.GetVideoEncoderConfigurations) (media.GetVideoEncoderConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoEncoderConfigurationsResponse media.GetVideoEncoderConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoEncoderConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoEncoderConfigurations") - return reply.Body.GetVideoEncoderConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoSourceConfigurationOptions_auto.go b/sdk/media/GetVideoSourceConfigurationOptions_auto.go deleted file mode 100644 index a079f98..0000000 --- a/sdk/media/GetVideoSourceConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoSourceConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceConfigurationOptionsResponse. -func Call_GetVideoSourceConfigurationOptions(ctx context.Context, dev *onvif.Device, request media.GetVideoSourceConfigurationOptions) (media.GetVideoSourceConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoSourceConfigurationOptionsResponse media.GetVideoSourceConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoSourceConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoSourceConfigurationOptions") - return reply.Body.GetVideoSourceConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoSourceConfiguration_auto.go b/sdk/media/GetVideoSourceConfiguration_auto.go deleted file mode 100644 index ef7c170..0000000 --- a/sdk/media/GetVideoSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceConfigurationResponse. -func Call_GetVideoSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.GetVideoSourceConfiguration) (media.GetVideoSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoSourceConfigurationResponse media.GetVideoSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoSourceConfiguration") - return reply.Body.GetVideoSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoSourceConfigurations_auto.go b/sdk/media/GetVideoSourceConfigurations_auto.go deleted file mode 100644 index 11b3dda..0000000 --- a/sdk/media/GetVideoSourceConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoSourceConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceConfigurationsResponse. -func Call_GetVideoSourceConfigurations(ctx context.Context, dev *onvif.Device, request media.GetVideoSourceConfigurations) (media.GetVideoSourceConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoSourceConfigurationsResponse media.GetVideoSourceConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoSourceConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoSourceConfigurations") - return reply.Body.GetVideoSourceConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoSourceModes_auto.go b/sdk/media/GetVideoSourceModes_auto.go deleted file mode 100644 index 3ddc489..0000000 --- a/sdk/media/GetVideoSourceModes_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoSourceModes forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourceModesResponse. -func Call_GetVideoSourceModes(ctx context.Context, dev *onvif.Device, request media.GetVideoSourceModes) (media.GetVideoSourceModesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoSourceModesResponse media.GetVideoSourceModesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoSourceModesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoSourceModes") - return reply.Body.GetVideoSourceModesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/GetVideoSources_auto.go b/sdk/media/GetVideoSources_auto.go deleted file mode 100644 index 9fadfaf..0000000 --- a/sdk/media/GetVideoSources_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_GetVideoSources forwards the call to dev.CallMethod() then parses the payload of the reply as a GetVideoSourcesResponse. -func Call_GetVideoSources(ctx context.Context, dev *onvif.Device, request media.GetVideoSources) (media.GetVideoSourcesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetVideoSourcesResponse media.GetVideoSourcesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetVideoSourcesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetVideoSources") - return reply.Body.GetVideoSourcesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveAudioDecoderConfiguration_auto.go b/sdk/media/RemoveAudioDecoderConfiguration_auto.go deleted file mode 100644 index 6ab5e32..0000000 --- a/sdk/media/RemoveAudioDecoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioDecoderConfigurationResponse. -func Call_RemoveAudioDecoderConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveAudioDecoderConfiguration) (media.RemoveAudioDecoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveAudioDecoderConfigurationResponse media.RemoveAudioDecoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveAudioDecoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveAudioDecoderConfiguration") - return reply.Body.RemoveAudioDecoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveAudioEncoderConfiguration_auto.go b/sdk/media/RemoveAudioEncoderConfiguration_auto.go deleted file mode 100644 index 48db46d..0000000 --- a/sdk/media/RemoveAudioEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioEncoderConfigurationResponse. -func Call_RemoveAudioEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveAudioEncoderConfiguration) (media.RemoveAudioEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveAudioEncoderConfigurationResponse media.RemoveAudioEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveAudioEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveAudioEncoderConfiguration") - return reply.Body.RemoveAudioEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveAudioOutputConfiguration_auto.go b/sdk/media/RemoveAudioOutputConfiguration_auto.go deleted file mode 100644 index db04e13..0000000 --- a/sdk/media/RemoveAudioOutputConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioOutputConfigurationResponse. -func Call_RemoveAudioOutputConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveAudioOutputConfiguration) (media.RemoveAudioOutputConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveAudioOutputConfigurationResponse media.RemoveAudioOutputConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveAudioOutputConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveAudioOutputConfiguration") - return reply.Body.RemoveAudioOutputConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveAudioSourceConfiguration_auto.go b/sdk/media/RemoveAudioSourceConfiguration_auto.go deleted file mode 100644 index 95802e1..0000000 --- a/sdk/media/RemoveAudioSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveAudioSourceConfigurationResponse. -func Call_RemoveAudioSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveAudioSourceConfiguration) (media.RemoveAudioSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveAudioSourceConfigurationResponse media.RemoveAudioSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveAudioSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveAudioSourceConfiguration") - return reply.Body.RemoveAudioSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveMetadataConfiguration_auto.go b/sdk/media/RemoveMetadataConfiguration_auto.go deleted file mode 100644 index 8d3a5f7..0000000 --- a/sdk/media/RemoveMetadataConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveMetadataConfigurationResponse. -func Call_RemoveMetadataConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveMetadataConfiguration) (media.RemoveMetadataConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveMetadataConfigurationResponse media.RemoveMetadataConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveMetadataConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveMetadataConfiguration") - return reply.Body.RemoveMetadataConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemovePTZConfiguration_auto.go b/sdk/media/RemovePTZConfiguration_auto.go deleted file mode 100644 index 7f517f5..0000000 --- a/sdk/media/RemovePTZConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemovePTZConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemovePTZConfigurationResponse. -func Call_RemovePTZConfiguration(ctx context.Context, dev *onvif.Device, request media.RemovePTZConfiguration) (media.RemovePTZConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemovePTZConfigurationResponse media.RemovePTZConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemovePTZConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemovePTZConfiguration") - return reply.Body.RemovePTZConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go b/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go deleted file mode 100644 index 3b61521..0000000 --- a/sdk/media/RemoveVideoAnalyticsConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveVideoAnalyticsConfigurationResponse. -func Call_RemoveVideoAnalyticsConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveVideoAnalyticsConfiguration) (media.RemoveVideoAnalyticsConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveVideoAnalyticsConfigurationResponse media.RemoveVideoAnalyticsConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveVideoAnalyticsConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveVideoAnalyticsConfiguration") - return reply.Body.RemoveVideoAnalyticsConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveVideoEncoderConfiguration_auto.go b/sdk/media/RemoveVideoEncoderConfiguration_auto.go deleted file mode 100644 index df3e048..0000000 --- a/sdk/media/RemoveVideoEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveVideoEncoderConfigurationResponse. -func Call_RemoveVideoEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveVideoEncoderConfiguration) (media.RemoveVideoEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveVideoEncoderConfigurationResponse media.RemoveVideoEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveVideoEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveVideoEncoderConfiguration") - return reply.Body.RemoveVideoEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/RemoveVideoSourceConfiguration_auto.go b/sdk/media/RemoveVideoSourceConfiguration_auto.go deleted file mode 100644 index aaf6aca..0000000 --- a/sdk/media/RemoveVideoSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_RemoveVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a RemoveVideoSourceConfigurationResponse. -func Call_RemoveVideoSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.RemoveVideoSourceConfiguration) (media.RemoveVideoSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemoveVideoSourceConfigurationResponse media.RemoveVideoSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemoveVideoSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemoveVideoSourceConfiguration") - return reply.Body.RemoveVideoSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetAudioDecoderConfiguration_auto.go b/sdk/media/SetAudioDecoderConfiguration_auto.go deleted file mode 100644 index 0a9e4d9..0000000 --- a/sdk/media/SetAudioDecoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetAudioDecoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioDecoderConfigurationResponse. -func Call_SetAudioDecoderConfiguration(ctx context.Context, dev *onvif.Device, request media.SetAudioDecoderConfiguration) (media.SetAudioDecoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetAudioDecoderConfigurationResponse media.SetAudioDecoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetAudioDecoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetAudioDecoderConfiguration") - return reply.Body.SetAudioDecoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetAudioEncoderConfiguration_auto.go b/sdk/media/SetAudioEncoderConfiguration_auto.go deleted file mode 100644 index 2c9c281..0000000 --- a/sdk/media/SetAudioEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetAudioEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioEncoderConfigurationResponse. -func Call_SetAudioEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.SetAudioEncoderConfiguration) (media.SetAudioEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetAudioEncoderConfigurationResponse media.SetAudioEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetAudioEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetAudioEncoderConfiguration") - return reply.Body.SetAudioEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetAudioOutputConfiguration_auto.go b/sdk/media/SetAudioOutputConfiguration_auto.go deleted file mode 100644 index 41107f2..0000000 --- a/sdk/media/SetAudioOutputConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetAudioOutputConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioOutputConfigurationResponse. -func Call_SetAudioOutputConfiguration(ctx context.Context, dev *onvif.Device, request media.SetAudioOutputConfiguration) (media.SetAudioOutputConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetAudioOutputConfigurationResponse media.SetAudioOutputConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetAudioOutputConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetAudioOutputConfiguration") - return reply.Body.SetAudioOutputConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetAudioSourceConfiguration_auto.go b/sdk/media/SetAudioSourceConfiguration_auto.go deleted file mode 100644 index 81b092d..0000000 --- a/sdk/media/SetAudioSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetAudioSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetAudioSourceConfigurationResponse. -func Call_SetAudioSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.SetAudioSourceConfiguration) (media.SetAudioSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetAudioSourceConfigurationResponse media.SetAudioSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetAudioSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetAudioSourceConfiguration") - return reply.Body.SetAudioSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetMetadataConfiguration_auto.go b/sdk/media/SetMetadataConfiguration_auto.go deleted file mode 100644 index 6da2cb6..0000000 --- a/sdk/media/SetMetadataConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetMetadataConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetMetadataConfigurationResponse. -func Call_SetMetadataConfiguration(ctx context.Context, dev *onvif.Device, request media.SetMetadataConfiguration) (media.SetMetadataConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetMetadataConfigurationResponse media.SetMetadataConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetMetadataConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetMetadataConfiguration") - return reply.Body.SetMetadataConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetOSD_auto.go b/sdk/media/SetOSD_auto.go deleted file mode 100644 index 5dd12ea..0000000 --- a/sdk/media/SetOSD_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetOSD forwards the call to dev.CallMethod() then parses the payload of the reply as a SetOSDResponse. -func Call_SetOSD(ctx context.Context, dev *onvif.Device, request media.SetOSD) (media.SetOSDResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetOSDResponse media.SetOSDResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetOSDResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetOSD") - return reply.Body.SetOSDResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetSynchronizationPoint_auto.go b/sdk/media/SetSynchronizationPoint_auto.go deleted file mode 100644 index 8bc3289..0000000 --- a/sdk/media/SetSynchronizationPoint_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetSynchronizationPoint forwards the call to dev.CallMethod() then parses the payload of the reply as a SetSynchronizationPointResponse. -func Call_SetSynchronizationPoint(ctx context.Context, dev *onvif.Device, request media.SetSynchronizationPoint) (media.SetSynchronizationPointResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetSynchronizationPointResponse media.SetSynchronizationPointResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetSynchronizationPointResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetSynchronizationPoint") - return reply.Body.SetSynchronizationPointResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetVideoAnalyticsConfiguration_auto.go b/sdk/media/SetVideoAnalyticsConfiguration_auto.go deleted file mode 100644 index 6bc2b83..0000000 --- a/sdk/media/SetVideoAnalyticsConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetVideoAnalyticsConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoAnalyticsConfigurationResponse. -func Call_SetVideoAnalyticsConfiguration(ctx context.Context, dev *onvif.Device, request media.SetVideoAnalyticsConfiguration) (media.SetVideoAnalyticsConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetVideoAnalyticsConfigurationResponse media.SetVideoAnalyticsConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetVideoAnalyticsConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetVideoAnalyticsConfiguration") - return reply.Body.SetVideoAnalyticsConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetVideoEncoderConfiguration_auto.go b/sdk/media/SetVideoEncoderConfiguration_auto.go deleted file mode 100644 index 6aca6cf..0000000 --- a/sdk/media/SetVideoEncoderConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetVideoEncoderConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoEncoderConfigurationResponse. -func Call_SetVideoEncoderConfiguration(ctx context.Context, dev *onvif.Device, request media.SetVideoEncoderConfiguration) (media.SetVideoEncoderConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetVideoEncoderConfigurationResponse media.SetVideoEncoderConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetVideoEncoderConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetVideoEncoderConfiguration") - return reply.Body.SetVideoEncoderConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetVideoSourceConfiguration_auto.go b/sdk/media/SetVideoSourceConfiguration_auto.go deleted file mode 100644 index 5cc60e6..0000000 --- a/sdk/media/SetVideoSourceConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetVideoSourceConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoSourceConfigurationResponse. -func Call_SetVideoSourceConfiguration(ctx context.Context, dev *onvif.Device, request media.SetVideoSourceConfiguration) (media.SetVideoSourceConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetVideoSourceConfigurationResponse media.SetVideoSourceConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetVideoSourceConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetVideoSourceConfiguration") - return reply.Body.SetVideoSourceConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/SetVideoSourceMode_auto.go b/sdk/media/SetVideoSourceMode_auto.go deleted file mode 100644 index 985be97..0000000 --- a/sdk/media/SetVideoSourceMode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_SetVideoSourceMode forwards the call to dev.CallMethod() then parses the payload of the reply as a SetVideoSourceModeResponse. -func Call_SetVideoSourceMode(ctx context.Context, dev *onvif.Device, request media.SetVideoSourceMode) (media.SetVideoSourceModeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetVideoSourceModeResponse media.SetVideoSourceModeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetVideoSourceModeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetVideoSourceMode") - return reply.Body.SetVideoSourceModeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/StartMulticastStreaming_auto.go b/sdk/media/StartMulticastStreaming_auto.go deleted file mode 100644 index 1e6f1ae..0000000 --- a/sdk/media/StartMulticastStreaming_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_StartMulticastStreaming forwards the call to dev.CallMethod() then parses the payload of the reply as a StartMulticastStreamingResponse. -func Call_StartMulticastStreaming(ctx context.Context, dev *onvif.Device, request media.StartMulticastStreaming) (media.StartMulticastStreamingResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - StartMulticastStreamingResponse media.StartMulticastStreamingResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.StartMulticastStreamingResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "StartMulticastStreaming") - return reply.Body.StartMulticastStreamingResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/StopMulticastStreaming_auto.go b/sdk/media/StopMulticastStreaming_auto.go deleted file mode 100644 index 477ad40..0000000 --- a/sdk/media/StopMulticastStreaming_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package media - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/media" -) - -// Call_StopMulticastStreaming forwards the call to dev.CallMethod() then parses the payload of the reply as a StopMulticastStreamingResponse. -func Call_StopMulticastStreaming(ctx context.Context, dev *onvif.Device, request media.StopMulticastStreaming) (media.StopMulticastStreamingResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - StopMulticastStreamingResponse media.StopMulticastStreamingResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.StopMulticastStreamingResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "StopMulticastStreaming") - return reply.Body.StopMulticastStreamingResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/media/media.go b/sdk/media/media.go deleted file mode 100644 index 4ce49d7..0000000 --- a/sdk/media/media.go +++ /dev/null @@ -1,81 +0,0 @@ -package media - -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetServiceCapabilities -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSources -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSources -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputs -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media CreateProfile -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetProfile -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetProfiles -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddVideoEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveVideoEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddVideoSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveVideoSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddPTZConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemovePTZConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddVideoAnalyticsConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveVideoAnalyticsConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddMetadataConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveMetadataConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioOutputConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioOutputConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media AddAudioDecoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media RemoveAudioDecoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media DeleteProfile -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoEncoderConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSourceConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioEncoderConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoAnalyticsConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetMetadataConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioDecoderConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoAnalyticsConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetMetadataConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioDecoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleVideoEncoderConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleVideoSourceConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioEncoderConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioSourceConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleVideoAnalyticsConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleMetadataConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioOutputConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetCompatibleAudioDecoderConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioSourceConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioEncoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoAnalyticsConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetMetadataConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioOutputConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetAudioDecoderConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoEncoderConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioSourceConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioEncoderConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetMetadataConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioOutputConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetAudioDecoderConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetGuaranteedNumberOfVideoEncoderInstances -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetStreamUri -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media StartMulticastStreaming -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media StopMulticastStreaming -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetSynchronizationPoint -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetSnapshotUri -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetVideoSourceModes -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetVideoSourceMode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetOSDs -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetOSD -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media GetOSDOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media SetOSD -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media CreateOSD -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen media media DeleteOSD diff --git a/sdk/ptz/AbsoluteMove_auto.go b/sdk/ptz/AbsoluteMove_auto.go deleted file mode 100644 index 61a877c..0000000 --- a/sdk/ptz/AbsoluteMove_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_AbsoluteMove forwards the call to dev.CallMethod() then parses the payload of the reply as a AbsoluteMoveResponse. -func Call_AbsoluteMove(ctx context.Context, dev *onvif.Device, request ptz.AbsoluteMove) (ptz.AbsoluteMoveResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - AbsoluteMoveResponse ptz.AbsoluteMoveResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.AbsoluteMoveResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "AbsoluteMove") - return reply.Body.AbsoluteMoveResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/ContinuousMove_auto.go b/sdk/ptz/ContinuousMove_auto.go deleted file mode 100644 index beaf9ab..0000000 --- a/sdk/ptz/ContinuousMove_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_ContinuousMove forwards the call to dev.CallMethod() then parses the payload of the reply as a ContinuousMoveResponse. -func Call_ContinuousMove(ctx context.Context, dev *onvif.Device, request ptz.ContinuousMove) (ptz.ContinuousMoveResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - ContinuousMoveResponse ptz.ContinuousMoveResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.ContinuousMoveResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "ContinuousMove") - return reply.Body.ContinuousMoveResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/CreatePresetTour_auto.go b/sdk/ptz/CreatePresetTour_auto.go deleted file mode 100644 index 43bea08..0000000 --- a/sdk/ptz/CreatePresetTour_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_CreatePresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a CreatePresetTourResponse. -func Call_CreatePresetTour(ctx context.Context, dev *onvif.Device, request ptz.CreatePresetTour) (ptz.CreatePresetTourResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - CreatePresetTourResponse ptz.CreatePresetTourResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.CreatePresetTourResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "CreatePresetTour") - return reply.Body.CreatePresetTourResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GeoMove_auto.go b/sdk/ptz/GeoMove_auto.go deleted file mode 100644 index 6c70105..0000000 --- a/sdk/ptz/GeoMove_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GeoMove forwards the call to dev.CallMethod() then parses the payload of the reply as a GeoMoveResponse. -func Call_GeoMove(ctx context.Context, dev *onvif.Device, request ptz.GeoMove) (ptz.GeoMoveResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GeoMoveResponse ptz.GeoMoveResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GeoMoveResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GeoMove") - return reply.Body.GeoMoveResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetCompatibleConfigurations_auto.go b/sdk/ptz/GetCompatibleConfigurations_auto.go deleted file mode 100644 index 8673ee2..0000000 --- a/sdk/ptz/GetCompatibleConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetCompatibleConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetCompatibleConfigurationsResponse. -func Call_GetCompatibleConfigurations(ctx context.Context, dev *onvif.Device, request ptz.GetCompatibleConfigurations) (ptz.GetCompatibleConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetCompatibleConfigurationsResponse ptz.GetCompatibleConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetCompatibleConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetCompatibleConfigurations") - return reply.Body.GetCompatibleConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetConfigurationOptions_auto.go b/sdk/ptz/GetConfigurationOptions_auto.go deleted file mode 100644 index e9abd7d..0000000 --- a/sdk/ptz/GetConfigurationOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetConfigurationOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetConfigurationOptionsResponse. -func Call_GetConfigurationOptions(ctx context.Context, dev *onvif.Device, request ptz.GetConfigurationOptions) (ptz.GetConfigurationOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetConfigurationOptionsResponse ptz.GetConfigurationOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetConfigurationOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetConfigurationOptions") - return reply.Body.GetConfigurationOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetConfiguration_auto.go b/sdk/ptz/GetConfiguration_auto.go deleted file mode 100644 index 852308e..0000000 --- a/sdk/ptz/GetConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a GetConfigurationResponse. -func Call_GetConfiguration(ctx context.Context, dev *onvif.Device, request ptz.GetConfiguration) (ptz.GetConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetConfigurationResponse ptz.GetConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetConfiguration") - return reply.Body.GetConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetConfigurations_auto.go b/sdk/ptz/GetConfigurations_auto.go deleted file mode 100644 index 41da137..0000000 --- a/sdk/ptz/GetConfigurations_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetConfigurations forwards the call to dev.CallMethod() then parses the payload of the reply as a GetConfigurationsResponse. -func Call_GetConfigurations(ctx context.Context, dev *onvif.Device, request ptz.GetConfigurations) (ptz.GetConfigurationsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetConfigurationsResponse ptz.GetConfigurationsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetConfigurationsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetConfigurations") - return reply.Body.GetConfigurationsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetNode_auto.go b/sdk/ptz/GetNode_auto.go deleted file mode 100644 index 7f46952..0000000 --- a/sdk/ptz/GetNode_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetNode forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNodeResponse. -func Call_GetNode(ctx context.Context, dev *onvif.Device, request ptz.GetNode) (ptz.GetNodeResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetNodeResponse ptz.GetNodeResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetNodeResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetNode") - return reply.Body.GetNodeResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetNodes_auto.go b/sdk/ptz/GetNodes_auto.go deleted file mode 100644 index 84699b0..0000000 --- a/sdk/ptz/GetNodes_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetNodes forwards the call to dev.CallMethod() then parses the payload of the reply as a GetNodesResponse. -func Call_GetNodes(ctx context.Context, dev *onvif.Device, request ptz.GetNodes) (ptz.GetNodesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetNodesResponse ptz.GetNodesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetNodesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetNodes") - return reply.Body.GetNodesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetPresetTourOptions_auto.go b/sdk/ptz/GetPresetTourOptions_auto.go deleted file mode 100644 index 2a63618..0000000 --- a/sdk/ptz/GetPresetTourOptions_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetPresetTourOptions forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetTourOptionsResponse. -func Call_GetPresetTourOptions(ctx context.Context, dev *onvif.Device, request ptz.GetPresetTourOptions) (ptz.GetPresetTourOptionsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetPresetTourOptionsResponse ptz.GetPresetTourOptionsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetPresetTourOptionsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetPresetTourOptions") - return reply.Body.GetPresetTourOptionsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetPresetTour_auto.go b/sdk/ptz/GetPresetTour_auto.go deleted file mode 100644 index 15fef28..0000000 --- a/sdk/ptz/GetPresetTour_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetPresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetTourResponse. -func Call_GetPresetTour(ctx context.Context, dev *onvif.Device, request ptz.GetPresetTour) (ptz.GetPresetTourResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetPresetTourResponse ptz.GetPresetTourResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetPresetTourResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetPresetTour") - return reply.Body.GetPresetTourResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetPresetTours_auto.go b/sdk/ptz/GetPresetTours_auto.go deleted file mode 100644 index ea84858..0000000 --- a/sdk/ptz/GetPresetTours_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetPresetTours forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetToursResponse. -func Call_GetPresetTours(ctx context.Context, dev *onvif.Device, request ptz.GetPresetTours) (ptz.GetPresetToursResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetPresetToursResponse ptz.GetPresetToursResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetPresetToursResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetPresetTours") - return reply.Body.GetPresetToursResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetPresets_auto.go b/sdk/ptz/GetPresets_auto.go deleted file mode 100644 index 1273f5c..0000000 --- a/sdk/ptz/GetPresets_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetPresets forwards the call to dev.CallMethod() then parses the payload of the reply as a GetPresetsResponse. -func Call_GetPresets(ctx context.Context, dev *onvif.Device, request ptz.GetPresets) (ptz.GetPresetsResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetPresetsResponse ptz.GetPresetsResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetPresetsResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetPresets") - return reply.Body.GetPresetsResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetServiceCapabilities_auto.go b/sdk/ptz/GetServiceCapabilities_auto.go deleted file mode 100644 index 4c6b7a3..0000000 --- a/sdk/ptz/GetServiceCapabilities_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetServiceCapabilities forwards the call to dev.CallMethod() then parses the payload of the reply as a GetServiceCapabilitiesResponse. -func Call_GetServiceCapabilities(ctx context.Context, dev *onvif.Device, request ptz.GetServiceCapabilities) (ptz.GetServiceCapabilitiesResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetServiceCapabilitiesResponse ptz.GetServiceCapabilitiesResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetServiceCapabilities") - return reply.Body.GetServiceCapabilitiesResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GetStatus_auto.go b/sdk/ptz/GetStatus_auto.go deleted file mode 100644 index 433df4d..0000000 --- a/sdk/ptz/GetStatus_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GetStatus forwards the call to dev.CallMethod() then parses the payload of the reply as a GetStatusResponse. -func Call_GetStatus(ctx context.Context, dev *onvif.Device, request ptz.GetStatus) (ptz.GetStatusResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GetStatusResponse ptz.GetStatusResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GetStatusResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GetStatus") - return reply.Body.GetStatusResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GotoHomePosition_auto.go b/sdk/ptz/GotoHomePosition_auto.go deleted file mode 100644 index ce6c22d..0000000 --- a/sdk/ptz/GotoHomePosition_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GotoHomePosition forwards the call to dev.CallMethod() then parses the payload of the reply as a GotoHomePositionResponse. -func Call_GotoHomePosition(ctx context.Context, dev *onvif.Device, request ptz.GotoHomePosition) (ptz.GotoHomePositionResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GotoHomePositionResponse ptz.GotoHomePositionResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GotoHomePositionResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GotoHomePosition") - return reply.Body.GotoHomePositionResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/GotoPreset_auto.go b/sdk/ptz/GotoPreset_auto.go deleted file mode 100644 index 28dac13..0000000 --- a/sdk/ptz/GotoPreset_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_GotoPreset forwards the call to dev.CallMethod() then parses the payload of the reply as a GotoPresetResponse. -func Call_GotoPreset(ctx context.Context, dev *onvif.Device, request ptz.GotoPreset) (ptz.GotoPresetResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - GotoPresetResponse ptz.GotoPresetResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.GotoPresetResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "GotoPreset") - return reply.Body.GotoPresetResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/ModifyPresetTour_auto.go b/sdk/ptz/ModifyPresetTour_auto.go deleted file mode 100644 index 2d0d6e0..0000000 --- a/sdk/ptz/ModifyPresetTour_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_ModifyPresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a ModifyPresetTourResponse. -func Call_ModifyPresetTour(ctx context.Context, dev *onvif.Device, request ptz.ModifyPresetTour) (ptz.ModifyPresetTourResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - ModifyPresetTourResponse ptz.ModifyPresetTourResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.ModifyPresetTourResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "ModifyPresetTour") - return reply.Body.ModifyPresetTourResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/OperatePresetTour_auto.go b/sdk/ptz/OperatePresetTour_auto.go deleted file mode 100644 index 9e278d0..0000000 --- a/sdk/ptz/OperatePresetTour_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_OperatePresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a OperatePresetTourResponse. -func Call_OperatePresetTour(ctx context.Context, dev *onvif.Device, request ptz.OperatePresetTour) (ptz.OperatePresetTourResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - OperatePresetTourResponse ptz.OperatePresetTourResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.OperatePresetTourResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "OperatePresetTour") - return reply.Body.OperatePresetTourResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/RelativeMove_auto.go b/sdk/ptz/RelativeMove_auto.go deleted file mode 100644 index 8a24189..0000000 --- a/sdk/ptz/RelativeMove_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_RelativeMove forwards the call to dev.CallMethod() then parses the payload of the reply as a RelativeMoveResponse. -func Call_RelativeMove(ctx context.Context, dev *onvif.Device, request ptz.RelativeMove) (ptz.RelativeMoveResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RelativeMoveResponse ptz.RelativeMoveResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RelativeMoveResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RelativeMove") - return reply.Body.RelativeMoveResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/RemovePresetTour_auto.go b/sdk/ptz/RemovePresetTour_auto.go deleted file mode 100644 index ed22049..0000000 --- a/sdk/ptz/RemovePresetTour_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_RemovePresetTour forwards the call to dev.CallMethod() then parses the payload of the reply as a RemovePresetTourResponse. -func Call_RemovePresetTour(ctx context.Context, dev *onvif.Device, request ptz.RemovePresetTour) (ptz.RemovePresetTourResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemovePresetTourResponse ptz.RemovePresetTourResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemovePresetTourResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemovePresetTour") - return reply.Body.RemovePresetTourResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/RemovePreset_auto.go b/sdk/ptz/RemovePreset_auto.go deleted file mode 100644 index ae4828f..0000000 --- a/sdk/ptz/RemovePreset_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_RemovePreset forwards the call to dev.CallMethod() then parses the payload of the reply as a RemovePresetResponse. -func Call_RemovePreset(ctx context.Context, dev *onvif.Device, request ptz.RemovePreset) (ptz.RemovePresetResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - RemovePresetResponse ptz.RemovePresetResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.RemovePresetResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "RemovePreset") - return reply.Body.RemovePresetResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/SendAuxiliaryCommand_auto.go b/sdk/ptz/SendAuxiliaryCommand_auto.go deleted file mode 100644 index 23a7fa4..0000000 --- a/sdk/ptz/SendAuxiliaryCommand_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_SendAuxiliaryCommand forwards the call to dev.CallMethod() then parses the payload of the reply as a SendAuxiliaryCommandResponse. -func Call_SendAuxiliaryCommand(ctx context.Context, dev *onvif.Device, request ptz.SendAuxiliaryCommand) (ptz.SendAuxiliaryCommandResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SendAuxiliaryCommandResponse ptz.SendAuxiliaryCommandResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SendAuxiliaryCommandResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SendAuxiliaryCommand") - return reply.Body.SendAuxiliaryCommandResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/SetConfiguration_auto.go b/sdk/ptz/SetConfiguration_auto.go deleted file mode 100644 index 262be18..0000000 --- a/sdk/ptz/SetConfiguration_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_SetConfiguration forwards the call to dev.CallMethod() then parses the payload of the reply as a SetConfigurationResponse. -func Call_SetConfiguration(ctx context.Context, dev *onvif.Device, request ptz.SetConfiguration) (ptz.SetConfigurationResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetConfigurationResponse ptz.SetConfigurationResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetConfigurationResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetConfiguration") - return reply.Body.SetConfigurationResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/SetHomePosition_auto.go b/sdk/ptz/SetHomePosition_auto.go deleted file mode 100644 index bef6888..0000000 --- a/sdk/ptz/SetHomePosition_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_SetHomePosition forwards the call to dev.CallMethod() then parses the payload of the reply as a SetHomePositionResponse. -func Call_SetHomePosition(ctx context.Context, dev *onvif.Device, request ptz.SetHomePosition) (ptz.SetHomePositionResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetHomePositionResponse ptz.SetHomePositionResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetHomePositionResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetHomePosition") - return reply.Body.SetHomePositionResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/SetPreset_auto.go b/sdk/ptz/SetPreset_auto.go deleted file mode 100644 index d319dbb..0000000 --- a/sdk/ptz/SetPreset_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_SetPreset forwards the call to dev.CallMethod() then parses the payload of the reply as a SetPresetResponse. -func Call_SetPreset(ctx context.Context, dev *onvif.Device, request ptz.SetPreset) (ptz.SetPresetResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - SetPresetResponse ptz.SetPresetResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.SetPresetResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "SetPreset") - return reply.Body.SetPresetResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/Stop_auto.go b/sdk/ptz/Stop_auto.go deleted file mode 100644 index dc21364..0000000 --- a/sdk/ptz/Stop_auto.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated : DO NOT EDIT. -// Copyright (c) 2022 Jean-Francois SMIGIELSKI -// Distributed under the MIT License - -package ptz - -import ( - "context" - "github.com/juju/errors" - "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/sdk" - "github.com/kerberos-io/onvif/ptz" -) - -// Call_Stop forwards the call to dev.CallMethod() then parses the payload of the reply as a StopResponse. -func Call_Stop(ctx context.Context, dev *onvif.Device, request ptz.Stop) (ptz.StopResponse, error) { - type Envelope struct { - Header struct{} - Body struct { - StopResponse ptz.StopResponse - } - } - var reply Envelope - if httpReply, err := dev.CallMethod(request); err != nil { - return reply.Body.StopResponse, errors.Annotate(err, "call") - } else { - err = sdk.ReadAndParse(ctx, httpReply, &reply, "Stop") - return reply.Body.StopResponse, errors.Annotate(err, "reply") - } -} diff --git a/sdk/ptz/ptz.go b/sdk/ptz/ptz.go deleted file mode 100644 index 40b7ee2..0000000 --- a/sdk/ptz/ptz.go +++ /dev/null @@ -1,30 +0,0 @@ -package ptz - -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetServiceCapabilities -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetNodes -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetNode -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetConfigurations -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SetConfiguration -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetConfigurationOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SendAuxiliaryCommand -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresets -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SetPreset -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz RemovePreset -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GotoPreset -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GotoHomePosition -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz SetHomePosition -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz ContinuousMove -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz RelativeMove -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetStatus -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz AbsoluteMove -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GeoMove -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz Stop -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresetTours -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresetTour -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetPresetTourOptions -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz CreatePresetTour -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz ModifyPresetTour -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz OperatePresetTour -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz RemovePresetTour -//go:generate go run github.com/kerberos-io/onvif/sdk/codegen ptz ptz GetCompatibleConfigurations diff --git a/sdk/sdk.go b/sdk/sdk.go deleted file mode 100644 index 0399b4e..0000000 --- a/sdk/sdk.go +++ /dev/null @@ -1,43 +0,0 @@ -package sdk - -import ( - "context" - "encoding/xml" - "io/ioutil" - "net/http" - "os" - "time" - - "github.com/juju/errors" - "github.com/rs/zerolog" -) - -var ( - // LoggerContext is the builder of a zerolog.Logger that is exposed to the application so that - // options at the CLI might alter the formatting and the output of the logs. - LoggerContext = zerolog. - New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}). - With().Timestamp() - - // Logger is a zerolog logger, that can be safely used from any part of the application. - // It gathers the format and the output. - Logger = LoggerContext.Logger() -) - -func ReadAndParse(ctx context.Context, httpReply *http.Response, reply interface{}, tag string) error { - Logger.Debug(). - Str("msg", httpReply.Status). - Int("status", httpReply.StatusCode). - Str("action", tag). - Msg("RPC") - // TODO(jfsmig): extract the deadline from ctx.Deadline() and apply it on the reply reading - b, err := ioutil.ReadAll(httpReply.Body) - if err != nil { - return errors.Annotate(err, "read") - } - - httpReply.Body.Close() - - err = xml.Unmarshal(b, reply) - return errors.Annotate(err, "decode") -} diff --git a/ws-discovery/networking.go b/ws-discovery/networking.go index 7c1dc4f..4013d40 100644 --- a/ws-discovery/networking.go +++ b/ws-discovery/networking.go @@ -9,87 +9,171 @@ package wsdiscovery * permission of Palanjyan Zhorzhik *******************************************************/ +// Copyright (C) 2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + import ( "errors" + "fmt" "net" + "net/http" + "net/url" "os" + "strings" "time" - "github.com/gofrs/uuid" + "github.com/beevik/etree" + "github.com/google/uuid" + "github.com/kerberos-io/onvif" "golang.org/x/net/ipv4" ) -const bufSize = 8192 +const ( + bufSize = 8192 +) -//SendProbe to device -func SendProbe(interfaceName string, scopes, types []string, namespaces map[string]string) ([]string, error) { - // Creating UUID Version 4 - uuidV4 := uuid.Must(uuid.NewV4()) - //fmt.Printf("UUIDv4: %s\n", uuidV4) +// GetAvailableDevicesAtSpecificEthernetInterface sends a ws-discovery Probe Message via +// UDP multicast to Discover NVT type Devices +func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) ([]onvif.Device, error) { + types := []string{"dn:NetworkVideoTransmitter"} + namespaces := map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl", "ds": "http://www.onvif.org/ver10/device/wsdl"} - probeSOAP := buildProbeMessage(uuidV4.String(), scopes, types, namespaces) - //probeSOAP = ` - // - //
- //http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe - //uuid:78a2ed98-bc1f-4b08-9668-094fcba81e35 - //http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous - //urn:schemas-xmlsoap-org:ws:2005:04:discovery - //
- // - //dp0:NetworkVideoTransmitter - // - // - //
` + probeResponses, err := SendProbe(interfaceName, nil, types, namespaces) + if err != nil { + return nil, fmt.Errorf("failed to probe: %w", err) + } - return sendUDPMulticast(probeSOAP.String(), interfaceName) + nvtDevices, err := DevicesFromProbeResponses(probeResponses) + if err != nil { + return nil, fmt.Errorf("failed to discover Onvif devices: %w", err) + } + + return nvtDevices, nil } -func sendUDPMulticast(msg string, interfaceName string) ([]string, error) { +func DevicesFromProbeResponses(probeResponses []string) ([]onvif.Device, error) { + nvtDevices := make([]onvif.Device, 0) + xaddrSet := make(map[string]struct{}) + for _, j := range probeResponses { + doc := etree.NewDocument() + if err := doc.ReadFromString(j); err != nil { + return nil, err + } + + probeMatches := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch") + for _, probeMatch := range probeMatches { + var xaddr string + if address := probeMatch.FindElement("./XAddrs"); address != nil { + u, err := url.Parse(address.Text()) + if err != nil { + // TODO: Add logger for fmt.Printf("Invalid XAddrs: %s\n", address.Text()) + continue + } + xaddr = u.Host + } + if _, dupe := xaddrSet[xaddr]; dupe { + // TODO: Add logger for fmt.Printf("Skipping duplicate XAddr: %s\n", xaddr) + continue + } + + var endpointRefAddress string + if ref := probeMatch.FindElement("./EndpointReference/Address"); ref != nil { + uuidElements := strings.Split(ref.Text(), ":") + endpointRefAddress = uuidElements[len(uuidElements)-1] + } + + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: xaddr, + EndpointRefAddress: endpointRefAddress, + HttpClient: &http.Client{ + Timeout: 2 * time.Second, + }, + }) + if err != nil { + // TODO: Add logger for fmt.Printf("Failed to connect to camera at %s: %s\n", xaddr, err.Error()) + continue + } + + var scopes []string + ref := probeMatch.FindElement("./Scopes") + if ref != nil { + scopes = strings.Split(ref.Text(), " ") + } + dev.SetDeviceInfoFromScopes(scopes) + + xaddrSet[xaddr] = struct{}{} + nvtDevices = append(nvtDevices, *dev) + // TODO: Add logger for fmt.Printf("Onvif WS-Discovery: Find Xaddr: %-25s EndpointRefAddress: %s\n", xaddr, string(endpointRefAddress)) + } + } + + return nvtDevices, nil +} + +// SendProbe to device +func SendProbe(interfaceName string, scopes, types []string, namespaces map[string]string) ([]string, error) { + probeSOAP := BuildProbeMessage(uuid.NewString(), scopes, types, namespaces) + return SendUDPMulticast(probeSOAP.String(), interfaceName) +} + +func SendUDPMulticast(msg string, interfaceName string) ([]string, error) { + var responses []string + data := []byte(msg) + c, err := net.ListenPacket("udp4", "0.0.0.0:0") if err != nil { return nil, err } defer c.Close() - iface, err := net.InterfaceByName(interfaceName) - if err != nil { - return nil, err - } - p := ipv4.NewPacketConn(c) + + // 239.255.255.250 port 3702 is the multicast address and port used by ws-discovery group := net.IPv4(239, 255, 255, 250) - if err := p.JoinGroup(iface, &net.UDPAddr{IP: group}); err != nil { - return nil, err - } + dest := &net.UDPAddr{IP: group, Port: 3702} - dst := &net.UDPAddr{IP: group, Port: 3702} - data := []byte(msg) - for _, ifi := range []*net.Interface{iface} { - if err := p.SetMulticastInterface(ifi); err != nil { - return nil, err - } - p.SetMulticastTTL(2) - if _, err := p.WriteTo(data, nil, dst); err != nil { - return nil, err + var iface *net.Interface + if interfaceName == "" { + iface = nil + } else { + iface, err = net.InterfaceByName(interfaceName) + if err != nil { + return nil, fmt.Errorf("failed to call InterfaceByName for interface %q: %w", interfaceName, err) } } - if err := p.SetReadDeadline(time.Now().Add(time.Second * 1)); err != nil { - return nil, err + if err = p.JoinGroup(iface, &net.UDPAddr{IP: group}); err != nil { + return nil, fmt.Errorf("failed to JoinGroup for ws-discovery: %w", err) + } + if iface != nil { + if err = p.SetMulticastInterface(iface); err != nil { + return nil, fmt.Errorf("failed to SetMulticastInterface for interface %q: %w", interfaceName, err) + } + if err = p.SetMulticastTTL(2); err != nil { + return nil, fmt.Errorf("failed to SetMulticastTTL: %w", err) + } + } + if _, err = p.WriteTo(data, nil, dest); err != nil { + return nil, fmt.Errorf("failed to write to ws-discovery multicast address %s: %w", dest.String(), err) } - var result []string + if err = p.SetReadDeadline(time.Now().Add(time.Second * 1)); err != nil { + return nil, fmt.Errorf("failed to set read deadline: %w", err) + } + + b := make([]byte, bufSize) + + // keep reading from the PacketConn until the read deadline expires or an error occurs for { - b := make([]byte, bufSize) n, _, _, err := p.ReadFrom(b) if err != nil { + // ErrDeadlineExceeded is expected once the read timeout is expired if !errors.Is(err, os.ErrDeadlineExceeded) { - return nil, err + return nil, fmt.Errorf("unexpected error occurred while reading ws-discovery responses: %w", err) } break } - result = append(result, string(b[0:n])) + responses = append(responses, string(b[0:n])) } - return result, nil + return responses, nil } diff --git a/ws-discovery/networking_test.go b/ws-discovery/networking_test.go new file mode 100644 index 0000000..a845db2 --- /dev/null +++ b/ws-discovery/networking_test.go @@ -0,0 +1,56 @@ +package wsdiscovery + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDevicesFromProbeResponses(t *testing.T) { + probeResponses := []string{ + ` + + + + + + urn:uuid:cea94000-fb96-11b3-8260-686dbc5cb15d + + dn:NetworkVideoTransmitter tds:Device + onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/Profile/Streaming onvif://www.onvif.org/MAC/68:6d:bc:5c:b1:5d onvif://www.onvif.org/hardware/DFI6256TE http:123 + http://192.168.12.123/onvif/device_service + 10 + + + + `, + ` + + + + + + uuid:3fa1fe68-b915-4053-a3e1-c006c3afec0e + + + ttl + + tdn:NetworkVideoTransmitter + onvif://www.onvif.org/name/TP-IPC onvif://www.onvif.org/hardware/MODEL onvif://www.onvif.org/Profile/Streaming onvif://www.onvif.org/location/ShenZhen onvif://www.onvif.org/type/NetworkVideoTransmitter + http://192.168.12.128:2020/onvif/device_service + 1 + + + + `, + } + + devices, err := DevicesFromProbeResponses(probeResponses) + require.NoError(t, err) + require.Equal(t, 2, len(devices)) + assert.Equal(t, devices[0].GetDeviceParams().Xaddr, "192.168.12.123") + assert.Equal(t, devices[0].GetDeviceParams().EndpointRefAddress, "cea94000-fb96-11b3-8260-686dbc5cb15d") + assert.Equal(t, devices[1].GetDeviceParams().Xaddr, "192.168.12.128:2020") + assert.Equal(t, devices[1].GetDeviceParams().EndpointRefAddress, "3fa1fe68-b915-4053-a3e1-c006c3afec0e") +} diff --git a/ws-discovery/ws-discovery.go b/ws-discovery/ws-discovery.go index e6c6293..450dfd8 100644 --- a/ws-discovery/ws-discovery.go +++ b/ws-discovery/ws-discovery.go @@ -7,22 +7,41 @@ import ( "github.com/kerberos-io/onvif/gosoap" ) -func buildProbeMessage(uuidV4 string, scopes, types []string, nmsp map[string]string) gosoap.SoapMessage { - //Список namespace +// BuildProbeMessage generates a SOAP ws-discovery Probe message +// +// Example Message: +// +// +// +// +// http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe +// uuid:a277f13a-ecae-4492-9d6a-218982122d1c +// urn:schemas-xmlsoap-org:ws:2005:04:discovery +// +// +// +// dn:NetworkVideoTransmitter +// +// +// +func BuildProbeMessage(uuidV4 string, scopes, types []string, nmsp map[string]string) gosoap.SoapMessage { + // Namespace List namespaces := make(map[string]string) namespaces["a"] = "http://schemas.xmlsoap.org/ws/2004/08/addressing" - //namespaces["d"] = "http://schemas.xmlsoap.org/ws/2005/04/discovery" + namespaces["d"] = "http://schemas.xmlsoap.org/ws/2005/04/discovery" + namespaces["dn"] = "http://www.onvif.org/ver10/network/wsdl" probeMessage := gosoap.NewEmptySOAP() - probeMessage.AddRootNamespaces(namespaces) - //if len(nmsp) != 0 { - // probeMessage.AddRootNamespaces(nmsp) - //} + if len(nmsp) != 0 { + probeMessage.AddRootNamespaces(nmsp) + } - //fmt.Println(probeMessage.String()) - - //Содержимое Head + // Probe Header var headerContent []*etree.Element action := etree.NewElement("a:Action") @@ -32,49 +51,25 @@ func buildProbeMessage(uuidV4 string, scopes, types []string, nmsp map[string]st msgID := etree.NewElement("a:MessageID") msgID.SetText("uuid:" + uuidV4) - replyTo := etree.NewElement("a:ReplyTo") - replyTo.CreateElement("a:Address").SetText("http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous") - to := etree.NewElement("a:To") to.SetText("urn:schemas-xmlsoap-org:ws:2005:04:discovery") to.CreateAttr("mustUnderstand", "1") - headerContent = append(headerContent, action, msgID, replyTo, to) + headerContent = append(headerContent, action, msgID, to) probeMessage.AddHeaderContents(headerContent) - //Содержимое Body - probe := etree.NewElement("Probe") - probe.CreateAttr("xmlns", "http://schemas.xmlsoap.org/ws/2005/04/discovery") + // Probe Body + probe := etree.NewElement("d:Probe") if len(types) != 0 { typesTag := etree.NewElement("d:Types") - if len(nmsp) != 0 { - for key, value := range nmsp { - typesTag.CreateAttr("xmlns:"+key, value) - } - } - typesTag.CreateAttr("xmlns:d", "http://schemas.xmlsoap.org/ws/2005/04/discovery") - //typesTag.CreateAttr("xmlns:dp0", "http://www.onvif.org/ver10/network/wsdl") - var typesString string - for _, j := range types { - typesString += j - typesString += " " - } - - typesTag.SetText(strings.TrimSpace(typesString)) - + typesTag.SetText(strings.Join(types, " ")) probe.AddChild(typesTag) } if len(scopes) != 0 { scopesTag := etree.NewElement("d:Scopes") - var scopesString string - for _, j := range scopes { - scopesString += j - scopesString += " " - } - scopesTag.SetText(strings.TrimSpace(scopesString)) - + scopesTag.SetText(strings.Join(scopes, " ")) probe.AddChild(scopesTag) } diff --git a/xsd/built_in.go b/xsd/built_in.go index 2592b0a..3ff59ca 100644 --- a/xsd/built_in.go +++ b/xsd/built_in.go @@ -29,128 +29,127 @@ type AnySimpleType string ***********************************************************/ /* - The string datatype represents character strings in XML. - The ·value space· of string is the set of finite-length sequences of characters. - String has the following constraining facets: - • length - • minLength - • maxLength - • pattern - • enumeration - • whiteSpace +The string datatype represents character strings in XML. +The ·value space· of string is the set of finite-length sequences of characters. +String has the following constraining facets: +• length +• minLength +• maxLength +• pattern +• enumeration +• whiteSpace - More info: https://www.w3.org/TR/xmlschema-2/#string +More info: https://www.w3.org/TR/xmlschema-2/#string - //TODO: valid/invalid character declaration and process restrictions +//TODO: valid/invalid character declaration and process restrictions */ type String string /* - Construct an instance of xsd String type +Construct an instance of xsd String type */ func (tp String) NewString(data string) String { return String(data) } /* - Boolean has the ·value space· required to support the mathematical concept of binary-valued logic: {true, false}. - Boolean has the following ·constraining facets·: - • pattern - • whiteSpace +Boolean has the ·value space· required to support the mathematical concept of binary-valued logic: {true, false}. +Boolean has the following ·constraining facets·: +• pattern +• whiteSpace - More info: https://www.w3.org/TR/xmlschema-2/#boolean +More info: https://www.w3.org/TR/xmlschema-2/#boolean - //TODO: process restrictions +//TODO: process restrictions */ type Boolean bool /* - Construct an instance of xsd Boolean type +Construct an instance of xsd Boolean type */ func (tp Boolean) NewBool(data bool) Boolean { return Boolean(data) } /* - Float is patterned after the IEEE single-precision 32-bit floating point type - Float has the following ·constraining facets·: - • pattern - • enumeration - • whiteSpace - • maxInclusive - • maxExclusive - • minInclusive - • minExclusive +Float is patterned after the IEEE single-precision 32-bit floating point type +Float has the following ·constraining facets·: +• pattern +• enumeration +• whiteSpace +• maxInclusive +• maxExclusive +• minInclusive +• minExclusive - More info: https://www.w3.org/TR/xmlschema-2/#float +More info: https://www.w3.org/TR/xmlschema-2/#float - //TODO: process restrictions +//TODO: process restrictions */ type Float float32 /* - Construct an instance of xsd Float type +Construct an instance of xsd Float type */ func (tp Float) NewFloat(data float32) Float { return Float(data) } /* - The double datatype is patterned after the IEEE double-precision 64-bit floating point type - Double has the following ·constraining facets·: - • pattern - • enumeration - • whiteSpace - • maxInclusive - • maxExclusive - • minInclusive - • minExclusive +The double datatype is patterned after the IEEE double-precision 64-bit floating point type +Double has the following ·constraining facets·: +• pattern +• enumeration +• whiteSpace +• maxInclusive +• maxExclusive +• minInclusive +• minExclusive - More info: https://www.w3.org/TR/xmlschema-2/#double +More info: https://www.w3.org/TR/xmlschema-2/#double - //TODO: process restrictions +//TODO: process restrictions */ type Double float64 /* - Construct an instance of xsd Double type +Construct an instance of xsd Double type */ func (tp Double) NewDouble(data float64) Double { return Double(data) } /* - The type decimal represents a decimal number of arbitrary precision. - Schema processors vary in the number of significant digits they support, - but a conforming processor must support a minimum of 18 significant digits. - The format of xsd:decimal is a sequence of digits optionally preceded by a sign ("+" or "-") - and optionally containing a period. The value may start or end with a period. - If the fractional part is 0 then the period and trailing zeros may be omitted. - Leading and trailing zeros are permitted, but they are not considered significant. - That is, the decimal values 3.0 and 3.0000 are considered equal. +The type decimal represents a decimal number of arbitrary precision. +Schema processors vary in the number of significant digits they support, +but a conforming processor must support a minimum of 18 significant digits. +The format of xsd:decimal is a sequence of digits optionally preceded by a sign ("+" or "-") +and optionally containing a period. The value may start or end with a period. +If the fractional part is 0 then the period and trailing zeros may be omitted. +Leading and trailing zeros are permitted, but they are not considered significant. +That is, the decimal values 3.0 and 3.0000 are considered equal. - Source: http://www.datypic.com/sc/xsd/t-xsd_decimal.html +Source: http://www.datypic.com/sc/xsd/t-xsd_decimal.html - Decimal has the following ·constraining facets·: - • totalDigits - • fractionDigits - • pattern - • whiteSpace - • enumeration - • maxInclusive - • maxExclusive - • minInclusive - • minExclusive +Decimal has the following ·constraining facets·: +• totalDigits +• fractionDigits +• pattern +• whiteSpace +• enumeration +• maxInclusive +• maxExclusive +• minInclusive +• minExclusive - More info: https://www.w3.org/TR/xmlschema-2/#decimal - - //TODO: process restrictions, valid/invalid characters(commas are not permitted; the decimal separator must be a period) +More info: https://www.w3.org/TR/xmlschema-2/#decimal +//TODO: process restrictions, valid/invalid characters(commas are not permitted; the decimal separator must be a period) */ type Decimal string /* - Construct an instance of xsd Decimal type +Construct an instance of xsd Decimal type */ func (tp Decimal) NewDecimal(data string) Decimal { return Decimal(data) @@ -180,11 +179,11 @@ func (tp Decimal) NewDecimal(data string) Decimal { TODO: Look at time.Duration go type */ -//Duration alias for AnySimpleType +// Duration alias for AnySimpleType type Duration AnySimpleType /* - Construct an instance of xsd duration type +Construct an instance of xsd duration type */ func (tp Duration) NewDateTime(years, months, days, hours, minutes, seconds string) Duration { i, err := iso8601.NewDuration( @@ -206,197 +205,192 @@ func (tp Duration) NewDateTime(years, months, days, hours, minutes, seconds stri } /* - DateTime values may be viewed as objects with integer-valued year, month, day, hour - and minute properties, a decimal-valued second property, and a boolean timezoned property. +DateTime values may be viewed as objects with integer-valued year, month, day, hour +and minute properties, a decimal-valued second property, and a boolean timezoned property. +The ·lexical space· of dateTime consists of finite-length sequences of characters of the form: - The ·lexical space· of dateTime consists of finite-length sequences of characters of the form: - '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?, + '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?, +DateTime has the following ·constraining facets·: - DateTime has the following ·constraining facets·: +• pattern +• enumeration +• whiteSpace +• maxInclusive +• maxExclusive +• minInclusive +• minExclusive - • pattern - • enumeration - • whiteSpace - • maxInclusive - • maxExclusive - • minInclusive - • minExclusive +More info: https://www.w3.org/TR/xmlschema-2/#dateTime - More info: https://www.w3.org/TR/xmlschema-2/#dateTime - - TODO: decide good type for time with proper format - TODO: process restrictions +TODO: decide good type for time with proper format +TODO: process restrictions */ -type DateTime AnySimpleType - -/* - Construct an instance of xsd dateTime type -*/ -func (tp DateTime) NewDateTime(time time.Time) DateTime { - return DateTime(time.Format("2002-10-10T12:00:00-05:00")) +type DateTime struct { + Time Time + Date Date } /* - Time represents an instant of time that recurs every day. - The ·value space· of time is the space of time of day values - as defined in § 5.3 of [ISO 8601]. Specifically, it is a set - of zero-duration daily time instances. +Time represents an instant of time that recurs every day. +The ·value space· of time is the space of time of day values +as defined in § 5.3 of [ISO 8601]. Specifically, it is a set +of zero-duration daily time instances. - Time has the following ·constraining facets·: +Time has the following ·constraining facets·: - • pattern - • enumeration - • whiteSpace - • maxInclusive - • maxExclusive - • minInclusive - • minExclusive +• pattern +• enumeration +• whiteSpace +• maxInclusive +• maxExclusive +• minInclusive +• minExclusive - More info: https://www.w3.org/TR/xmlschema-2/#time +More info: https://www.w3.org/TR/xmlschema-2/#time - TODO: process restrictions +TODO: process restrictions */ -type Time AnySimpleType +type Time struct { + Hour Int + Minute Int + Second Int +} /* Construct an instance of xsd time type */ -func (tp DateTime) NewTime(time time.Time) DateTime { - return DateTime(time.Format("15:04:05")) + +/* +The ·value space· of date consists of top-open intervals of +exactly one day in length on the timelines of dateTime, beginning +on the beginning moment of each day (in each timezone), +i.e. '00:00:00', up to but not including '24:00:00' +(which is identical with '00:00:00' of the next day). +For nontimezoned values, the top-open intervals disjointly +cover the nontimezoned timeline, one per day. For timezoned +values, the intervals begin at every minute and therefore overlap. +*/ +type Date struct { + Year Int + Month Int + Day Int } /* - The ·value space· of date consists of top-open intervals of - exactly one day in length on the timelines of dateTime, beginning - on the beginning moment of each day (in each timezone), - i.e. '00:00:00', up to but not including '24:00:00' - (which is identical with '00:00:00' of the next day). - For nontimezoned values, the top-open intervals disjointly - cover the nontimezoned timeline, one per day. For timezoned - values, the intervals begin at every minute and therefore overlap. -*/ -type Date AnySimpleType +The type xsd:gYearMonth represents a specific month of a specific +year. The letter g signifies "Gregorian." The format of +xsd:gYearMonth is CCYY-MM. No left truncation is allowed on +either part. To represents years later than 9999, additional +digits can be added to the left of the year value. +To represent years before 0001, a preceding minus sign ("-") +is permitted. -/* - Construct an instance of xsd date type -*/ -func (tp Date) NewDate(time time.Time) Date { - return Date(time.Format("2004-04-12-05:00")) -} +Source: http://www.datypic.com/sc/xsd/t-xsd_gYearMonth.html -/* - The type xsd:gYearMonth represents a specific month of a specific - year. The letter g signifies "Gregorian." The format of - xsd:gYearMonth is CCYY-MM. No left truncation is allowed on - either part. To represents years later than 9999, additional - digits can be added to the left of the year value. - To represent years before 0001, a preceding minus sign ("-") - is permitted. - - Source: http://www.datypic.com/sc/xsd/t-xsd_gYearMonth.html - - More info: https://www.w3.org/TR/xmlschema-2/#gYearMonth +More info: https://www.w3.org/TR/xmlschema-2/#gYearMonth */ type GYearMonth AnySimpleType /* - Construct an instance of xsd GYearMonth type +Construct an instance of xsd GYearMonth type */ -func (tp GYearMonth) NewGYearMonth(t time.Time) GYearMonth { - return GYearMonth(fmt.Sprintf("%4d-%2d", t.Year(), t.Month())) +func (tp GYearMonth) NewGYearMonth(time time.Time) GYearMonth { + return GYearMonth(fmt.Sprint("", time.Year(), "-", time.Month())) + //return GYearMonth(time.Format("2004-04-05:00")) } /* - The type xsd:gYear represents a specific calendar year. - The letter g signifies "Gregorian." The format of xsd:gYear - is CCYY. No left truncation is allowed. To represent years - later than 9999, additional digits can be added to the left - of the year value. To represent years before 0001, a preceding - minus sign ("-") is allowed. +The type xsd:gYear represents a specific calendar year. +The letter g signifies "Gregorian." The format of xsd:gYear +is CCYY. No left truncation is allowed. To represent years +later than 9999, additional digits can be added to the left +of the year value. To represent years before 0001, a preceding +minus sign ("-") is allowed. - Source: http://www.datypic.com/sc/xsd/t-xsd_gYear.html +Source: http://www.datypic.com/sc/xsd/t-xsd_gYear.html - More info: https://www.w3.org/TR/xmlschema-2/#gYear +More info: https://www.w3.org/TR/xmlschema-2/#gYear */ type GYear AnySimpleType /* - Construct an instance of xsd GYear type +Construct an instance of xsd GYear type */ -func (tp GYear) NewGYear(t time.Time) GYear { - return GYear(fmt.Sprintf("%4d", t.Year())) +func (tp GYear) NewGYear(time time.Time) GYear { + return GYear(fmt.Sprint("", time.Year())) + //return GYearMonth(time.Format("2004-04-05:00")) } /* - The type xsd:gMonthDay represents a specific day that recurs - every year. The letter g signifies "Gregorian." xsd:gMonthDay - can be used to say, for example, that your birthday is on the - 14th of April every year. The format of xsd:gMonthDay is --MM-DD. +The type xsd:gMonthDay represents a specific day that recurs +every year. The letter g signifies "Gregorian." xsd:gMonthDay +can be used to say, for example, that your birthday is on the +14th of April every year. The format of xsd:gMonthDay is --MM-DD. - Source: http://www.datypic.com/sc/xsd/t-xsd_gMonthDay.html +Source: http://www.datypic.com/sc/xsd/t-xsd_gMonthDay.html - More info: https://www.w3.org/TR/xmlschema-2/#gMonthDay +More info: https://www.w3.org/TR/xmlschema-2/#gMonthDay */ type GMonthDay AnySimpleType /* - Construct an instance of xsd GMonthDay type +Construct an instance of xsd GMonthDay type */ -func (tp GMonthDay) NewGMonthDay(t time.Time) GMonthDay { - return GMonthDay(fmt.Sprintf("--%2d-%2d", t.Month(), t.Day())) +func (tp GMonthDay) NewGMonthDay(time time.Time) GMonthDay { + return GMonthDay(fmt.Sprint("--", time.Month(), "-", time.Day())) } /* - The type xsd:gDay represents a day that recurs every month. - The letter g signifies "Gregorian." xsd:gDay can be used to say, - for example, that checks are paid on the 5th of each month. - To represent a duration of days, use the duration type instead. - The format of gDay is ---DD. +The type xsd:gDay represents a day that recurs every month. +The letter g signifies "Gregorian." xsd:gDay can be used to say, +for example, that checks are paid on the 5th of each month. +To represent a duration of days, use the duration type instead. +The format of gDay is ---DD. - Source: http://www.datypic.com/sc/xsd/t-xsd_gDay.html +Source: http://www.datypic.com/sc/xsd/t-xsd_gDay.html - More info: https://www.w3.org/TR/xmlschema-2/#gDay +More info: https://www.w3.org/TR/xmlschema-2/#gDay */ type GDay AnySimpleType /* - Construct an instance of xsd GDay type +Construct an instance of xsd GDay type */ -func (tp GDay) NewGDay(t time.Time) GDay { - return GDay(fmt.Sprintf("---%2d", t.Day())) +func (tp GDay) NewGDay(time time.Time) GDay { + return GDay(fmt.Sprint("---", time.Day())) } /* - The type xsd:gMonth represents a specific month that recurs - every year. The letter g signifies "Gregorian." xsd:gMonth - can be used to indicate, for example, that fiscal year-end - processing occurs in September of every year. To represent - a duration of months, use the duration type instead. The format - of xsd:gMonth is --MM. +The type xsd:gMonth represents a specific month that recurs +every year. The letter g signifies "Gregorian." xsd:gMonth +can be used to indicate, for example, that fiscal year-end +processing occurs in September of every year. To represent +a duration of months, use the duration type instead. The format +of xsd:gMonth is --MM. - Source: http://www.datypic.com/sc/xsd/t-xsd_gMonth.html +Source: http://www.datypic.com/sc/xsd/t-xsd_gMonth.html - More info: https://www.w3.org/TR/xmlschema-2/#gMonth +More info: https://www.w3.org/TR/xmlschema-2/#gMonth */ type GMonth AnySimpleType -func (tp GMonth) NewGMonth(t time.Time) GMonth { - return GMonth(fmt.Sprintf("--%2d", t.Month())) +func (tp GMonth) NewGMonth(time time.Time) GMonth { + return GMonth(fmt.Sprint("--", time.Month())) } /* - The xsd:hexBinary type represents binary data as a sequence - of binary octets. It uses hexadecimal encoding, where each - binary octet is a two-character hexadecimal number. - Lowercase and uppercase letters A through F are permitted. - For example, 0FB8 and 0fb8 are two equal xsd:hexBinary - representations consisting of two octets. +The xsd:hexBinary type represents binary data as a sequence +of binary octets. It uses hexadecimal encoding, where each +binary octet is a two-character hexadecimal number. +Lowercase and uppercase letters A through F are permitted. +For example, 0FB8 and 0fb8 are two equal xsd:hexBinary +representations consisting of two octets. - Source: http://www.datypic.com/sc/xsd/t-xsd_hexBinary.html +Source: http://www.datypic.com/sc/xsd/t-xsd_hexBinary.html - More info: https://www.w3.org/TR/xmlschema-2/#hexBinary +More info: https://www.w3.org/TR/xmlschema-2/#hexBinary */ type HexBinary AnySimpleType @@ -405,19 +399,19 @@ func (tp HexBinary) NewHexBinary(data []byte) HexBinary { } /* - base64Binary represents Base64-encoded arbitrary binary data. - The ·value space· of base64Binary is the set of finite-length sequences of binary octets. - For base64Binary data the entire binary stream is encoded using the Base64 Alphabet in [RFC 2045]. +base64Binary represents Base64-encoded arbitrary binary data. +The ·value space· of base64Binary is the set of finite-length sequences of binary octets. +For base64Binary data the entire binary stream is encoded using the Base64 Alphabet in [RFC 2045]. - base64Binary has the following ·constraining facets·: - • length - • minLength - • maxLength - • pattern - • enumeration - • whiteSpace +base64Binary has the following ·constraining facets·: +• length +• minLength +• maxLength +• pattern +• enumeration +• whiteSpace - More info: https://www.w3.org/TR/xmlschema-2/#base64Binary +More info: https://www.w3.org/TR/xmlschema-2/#base64Binary */ type Base64Binary AnySimpleType @@ -426,21 +420,21 @@ func (tp Base64Binary) NewBase64Binary(data []byte) Base64Binary { } /* - anyURI represents a Uniform Resource Identifier Reference (URI). - An anyURI value can be absolute or relative, and may have an optional - fragment identifier (i.e., it may be a URI Reference). - This type should be used to specify the intention that the - value fulfills the role of a URI as defined by [RFC 2396], as amended by [RFC 2732]. +anyURI represents a Uniform Resource Identifier Reference (URI). +An anyURI value can be absolute or relative, and may have an optional +fragment identifier (i.e., it may be a URI Reference). +This type should be used to specify the intention that the +value fulfills the role of a URI as defined by [RFC 2396], as amended by [RFC 2732]. - anyURI has the following ·constraining facets·: - • length - • minLength - • maxLength - • pattern - • enumeration - • whiteSpace +anyURI has the following ·constraining facets·: +• length +• minLength +• maxLength +• pattern +• enumeration +• whiteSpace - More info: https://www.w3.org/TR/xmlschema-2/#anyURI +More info: https://www.w3.org/TR/xmlschema-2/#anyURI */ type AnyURI AnySimpleType @@ -449,19 +443,19 @@ func (tp AnyURI) NewAnyURI(data url.URL) AnyURI { } /* - QName represents XML qualified names. The ·value space· of QName is the set of tuples - {namespace name, local part}, where namespace name is an anyURI and local part is an NCName. - The ·lexical space· of QName is the set of strings that ·match· the QName production of [Namespaces in XML]. +QName represents XML qualified names. The ·value space· of QName is the set of tuples +{namespace name, local part}, where namespace name is an anyURI and local part is an NCName. +The ·lexical space· of QName is the set of strings that ·match· the QName production of [Namespaces in XML]. - QName has the following ·constraining facets·: - • length - • minLength - • maxLength - • pattern - • enumeration - • whiteSpace +QName has the following ·constraining facets·: +• length +• minLength +• maxLength +• pattern +• enumeration +• whiteSpace - More info: https://www.w3.org/TR/xmlschema-2/#QName +More info: https://www.w3.org/TR/xmlschema-2/#QName */ type QName AnySimpleType @@ -484,7 +478,7 @@ func (tp QName) NewQName(prefix, local string) QName { type NormalizedString String -//TODO: check normalization +// TODO: check normalization func (tp NormalizedString) NewNormalizedString(data string) (NormalizedString, error) { if strings.ContainsAny(data, "\r\n\t<>&") { return NormalizedString(""), errors.New("String " + data + " contains forbidden symbols") @@ -520,7 +514,7 @@ func (tp Language) NewLanguage(data Token) (Language, error) { type NMTOKEN Token -//TODO: check for valid symbols: https://www.w3.org/TR/xml/#NT-Nmtoken +// TODO: check for valid symbols: https://www.w3.org/TR/xml/#NT-Nmtoken func (tp NMTOKEN) NewNMTOKEN(data string) NMTOKEN { return NMTOKEN(data) } @@ -537,19 +531,19 @@ func (tp NMTOKENS) NewNMTOKENS(data []NMTOKEN) NMTOKENS { type Name Token -//TODO: implements https://www.w3.org/TR/xml/#NT-Name +// TODO: implements https://www.w3.org/TR/xml/#NT-Name func (tp Name) NewName(data Token) Name { return Name(data) } type NCName Name -//TODO: https://www.w3.org/TR/REC-xml/#NT-Name and https://www.w3.org/TR/xml-names/#NT-NCName +// TODO: https://www.w3.org/TR/REC-xml/#NT-Name and https://www.w3.org/TR/xml-names/#NT-NCName func (tp NCName) NewNCName(data Name) NCName { return NCName(data) } -//TODO: improve next types to correspond to XMLSchema +// TODO: improve next types to correspond to XMLSchema type ID NCName func (tp ID) NewID(data NCName) ID { diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index 0eea49e..b973f06 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -59,20 +59,20 @@ type FloatRange struct { type OSDConfiguration struct { DeviceEntity `xml:"token,attr"` - VideoSourceConfigurationToken OSDReference `xml:"onvif:VideoSourceConfigurationToken"` - Type OSDType `xml:"onvif:Type"` - Position OSDPosConfiguration `xml:"onvif:Position"` - TextString OSDTextConfiguration `xml:"onvif:TextString"` - Image OSDImgConfiguration `xml:"onvif:Image"` - Extension OSDConfigurationExtension `xml:"onvif:Extension"` + VideoSourceConfigurationToken OSDReference `xml:"VideoSourceConfigurationToken"` + Type OSDType `xml:"Type"` + Position OSDPosConfiguration `xml:"Position"` + TextString OSDTextConfiguration `xml:"TextString"` + Image OSDImgConfiguration `xml:"Image"` + Extension OSDConfigurationExtension `xml:"Extension"` } type OSDType xsd.String type OSDPosConfiguration struct { - Type string `xml:"onvif:Type"` - Pos Vector `xml:"onvif:Pos"` - Extension OSDPosConfigurationExtension `xml:"onvif:Extension"` + Type string `xml:"Type"` + Pos Vector `xml:"Pos"` + Extension OSDPosConfigurationExtension `xml:"Extension"` } type Vector struct { @@ -87,20 +87,20 @@ type OSDReference ReferenceToken type OSDTextConfiguration struct { IsPersistentText xsd.Boolean `xml:"IsPersistentText,attr"` - Type xsd.String `xml:"onvif:Type"` - DateFormat xsd.String `xml:"onvif:DateFormat"` - TimeFormat xsd.String `xml:"onvif:TimeFormat"` - FontSize xsd.Int `xml:"onvif:FontSize"` - FontColor OSDColor `xml:"onvif:FontColor"` - BackgroundColor OSDColor `xml:"onvif:BackgroundColor"` - PlainText xsd.String `xml:"onvif:PlainText"` - Extension OSDTextConfigurationExtension `xml:"onvif:Extension"` + Type xsd.String `xml:"Type"` + DateFormat xsd.String `xml:"DateFormat"` + TimeFormat xsd.String `xml:"TimeFormat"` + FontSize xsd.Int `xml:"FontSize"` + FontColor OSDColor `xml:"FontColor"` + BackgroundColor OSDColor `xml:"BackgroundColor"` + PlainText xsd.String `xml:"PlainText"` + Extension OSDTextConfigurationExtension `xml:"Extension"` } type OSDColor struct { Transparent int `xml:"Transparent,attr"` - Color Color `xml:"onvif:Color"` + Color Color `xml:"Color"` } type Color struct { @@ -113,8 +113,8 @@ type Color struct { type OSDTextConfigurationExtension xsd.AnyType type OSDImgConfiguration struct { - ImgPath xsd.AnyURI `xml:"onvif:ImgPath"` - Extension OSDImgConfigurationExtension `xml:"onvif:Extension"` + ImgPath xsd.AnyURI `xml:"ImgPath"` + Extension OSDImgConfigurationExtension `xml:"Extension"` } type OSDImgConfigurationExtension xsd.AnyType @@ -130,8 +130,13 @@ type VideoSource struct { } type VideoResolution struct { - Width xsd.Int `xml:"onvif:Width"` - Height xsd.Int `xml:"onvif:Height"` + Width *xsd.Int `json:",omitempty"` + Height *xsd.Int `json:",omitempty"` +} + +type VideoResolutionRequest struct { + Width *xsd.Int `xml:"onvif:Width,omitempty"` + Height *xsd.Int `xml:"onvif:Height,omitempty"` } type ImagingSettings struct { @@ -193,8 +198,8 @@ type AutoFocusMode xsd.String type IrCutFilterMode xsd.String type WideDynamicRange struct { - Mode WideDynamicMode `xml:"onvif:Mode"` - Level float64 `xml:"onvif:Level"` + Mode WideDynamicMode `xml:"Mode"` + Level float64 `xml:"Level"` } type WideDynamicMode xsd.String @@ -215,72 +220,72 @@ type VideoSourceExtension struct { } type ImagingSettings20 struct { - BacklightCompensation *BacklightCompensation20 `xml:"onvif:BacklightCompensation"` - Brightness float64 `xml:"onvif:Brightness,omitempty"` - ColorSaturation float64 `xml:"onvif:ColorSaturation,omitempty"` - Contrast float64 `xml:"onvif:Contrast,omitempty"` - Exposure *Exposure20 `xml:"onvif:Exposure"` - Focus *FocusConfiguration20 `xml:"onvif:Focus"` - IrCutFilter *IrCutFilterMode `xml:"onvif:IrCutFilter"` - Sharpness float64 `xml:"onvif:Sharpness,omitempty"` - WideDynamicRange *WideDynamicRange20 `xml:"onvif:WideDynamicRange"` - WhiteBalance *WhiteBalance20 `xml:"onvif:WhiteBalance"` - Extension *ImagingSettingsExtension20 `xml:"onvif:Extension"` + BacklightCompensation BacklightCompensation20 `xml:"BacklightCompensation"` + Brightness float64 `xml:"Brightness"` + ColorSaturation float64 `xml:"ColorSaturation"` + Contrast float64 `xml:"Contrast"` + Exposure Exposure20 `xml:"Exposure"` + Focus FocusConfiguration20 `xml:"Focus"` + IrCutFilter IrCutFilterMode `xml:"IrCutFilter"` + Sharpness float64 `xml:"Sharpness"` + WideDynamicRange WideDynamicRange20 `xml:"WideDynamicRange"` + WhiteBalance WhiteBalance20 `xml:"WhiteBalance"` + Extension ImagingSettingsExtension20 `xml:"Extension"` } type BacklightCompensation20 struct { - Mode BacklightCompensationMode `xml:"onvif:Mode"` - Level float64 `xml:"onvif:Level"` + Mode BacklightCompensationMode `xml:"Mode"` + Level float64 `xml:"Level"` } type Exposure20 struct { - Mode ExposureMode `xml:"onvif:Mode,omitempty"` - Priority ExposurePriority `xml:"onvif:Priority,omitempty"` - Window Rectangle `xml:"onvif:Window,omitempty"` - MinExposureTime float64 `xml:"onvif:MinExposureTime,omitempty"` - MaxExposureTime float64 `xml:"onvif:MaxExposureTime,omitempty"` - MinGain float64 `xml:"onvif:MinGain,omitempty"` - MaxGain float64 `xml:"onvif:MaxGain,omitempty"` - MinIris float64 `xml:"onvif:MinIris,omitempty"` - MaxIris float64 `xml:"onvif:MaxIris,omitempty"` - ExposureTime float64 `xml:"onvif:ExposureTime,omitempty"` - Gain float64 `xml:"onvif:Gain,omitempty"` - Iris float64 `xml:"onvif:Iris,omitempty"` + Mode ExposureMode `xml:"Mode"` + Priority ExposurePriority `xml:"Priority"` + Window Rectangle `xml:"Window"` + MinExposureTime float64 `xml:"MinExposureTime"` + MaxExposureTime float64 `xml:"MaxExposureTime"` + MinGain float64 `xml:"MinGain"` + MaxGain float64 `xml:"MaxGain"` + MinIris float64 `xml:"MinIris"` + MaxIris float64 `xml:"MaxIris"` + ExposureTime float64 `xml:"ExposureTime"` + Gain float64 `xml:"Gain"` + Iris float64 `xml:"Iris"` } type FocusConfiguration20 struct { - AutoFocusMode AutoFocusMode `xml:"onvif:AutoFocusMode"` - DefaultSpeed float64 `xml:"onvif:DefaultSpeed"` - NearLimit float64 `xml:"onvif:NearLimit"` - FarLimit float64 `xml:"onvif:FarLimit"` - Extension FocusConfiguration20Extension `xml:"onvif:Extension"` + AutoFocusMode AutoFocusMode `xml:"AutoFocusMode"` + DefaultSpeed float64 `xml:"DefaultSpeed"` + NearLimit float64 `xml:"NearLimit"` + FarLimit float64 `xml:"FarLimit"` + Extension FocusConfiguration20Extension `xml:"Extension"` } type FocusConfiguration20Extension xsd.AnyType type WideDynamicRange20 struct { - Mode WideDynamicMode `xml:"onvif:Mode"` - Level float64 `xml:"onvif:Level"` + Mode WideDynamicMode `xml:"Mode"` + Level float64 `xml:"Level"` } type WhiteBalance20 struct { - Mode WhiteBalanceMode `xml:"onvif:Mode"` - CrGain float64 `xml:"onvif:CrGain"` - CbGain float64 `xml:"onvif:CbGain"` - Extension WhiteBalance20Extension `xml:"onvif:Extension"` + Mode WhiteBalanceMode `xml:"Mode"` + CrGain float64 `xml:"CrGain"` + CbGain float64 `xml:"CbGain"` + Extension WhiteBalance20Extension `xml:"Extension"` } type WhiteBalance20Extension xsd.AnyType type ImagingSettingsExtension20 struct { - ImageStabilization ImageStabilization `xml:"onvif:ImageStabilization"` - Extension ImagingSettingsExtension202 `xml:"onvif:Extension"` + ImageStabilization ImageStabilization `xml:"ImageStabilization"` + Extension ImagingSettingsExtension202 `xml:"Extension"` } type ImageStabilization struct { - Mode ImageStabilizationMode `xml:"onvif:Mode"` - Level float64 `xml:"onvif:Level"` - Extension ImageStabilizationExtension `xml:"onvif:Extension"` + Mode ImageStabilizationMode `xml:"Mode"` + Level float64 `xml:"Level"` + Extension ImageStabilizationExtension `xml:"Extension"` } type ImageStabilizationMode xsd.String @@ -288,30 +293,30 @@ type ImageStabilizationMode xsd.String type ImageStabilizationExtension xsd.AnyType type ImagingSettingsExtension202 struct { - IrCutFilterAutoAdjustment IrCutFilterAutoAdjustment `xml:"onvif:IrCutFilterAutoAdjustment"` - Extension ImagingSettingsExtension203 `xml:"onvif:Extension"` + IrCutFilterAutoAdjustment IrCutFilterAutoAdjustment `xml:"IrCutFilterAutoAdjustment"` + Extension ImagingSettingsExtension203 `xml:"Extension"` } type IrCutFilterAutoAdjustment struct { - BoundaryType string `xml:"onvif:BoundaryType"` - BoundaryOffset float64 `xml:"onvif:BoundaryOffset"` - ResponseTime xsd.Duration `xml:"onvif:ResponseTime"` - Extension IrCutFilterAutoAdjustmentExtension `xml:"onvif:Extension"` + BoundaryType string `xml:"BoundaryType"` + BoundaryOffset float64 `xml:"BoundaryOffset"` + ResponseTime xsd.Duration `xml:"ResponseTime"` + Extension IrCutFilterAutoAdjustmentExtension `xml:"Extension"` } type IrCutFilterAutoAdjustmentExtension xsd.AnyType type ImagingSettingsExtension203 struct { - ToneCompensation ToneCompensation `xml:"onvif:ToneCompensation"` - Defogging Defogging `xml:"onvif:Defogging"` - NoiseReduction NoiseReduction `xml:"onvif:NoiseReduction"` - Extension ImagingSettingsExtension204 `xml:"onvif:Extension"` + ToneCompensation ToneCompensation `xml:"ToneCompensation"` + Defogging Defogging `xml:"Defogging"` + NoiseReduction NoiseReduction `xml:"NoiseReduction"` + Extension ImagingSettingsExtension204 `xml:"Extension"` } type ToneCompensation struct { - Mode string `xml:"onvif:Mode"` - Level float64 `xml:"onvif:Level"` - Extension ToneCompensationExtension `xml:"onvif:Extension"` + Mode string `xml:"Mode"` + Level float64 `xml:"Level"` + Extension ToneCompensationExtension `xml:"Extension"` } type ToneCompensationExtension xsd.AnyType @@ -325,7 +330,7 @@ type Defogging struct { type DefoggingExtension xsd.AnyType type NoiseReduction struct { - Level float64 `xml:"onvif:Level"` + Level float64 `xml:"Level"` } type ImagingSettingsExtension204 xsd.AnyType @@ -345,39 +350,45 @@ type Profile struct { Token ReferenceToken `xml:"token,attr"` Fixed bool `xml:"fixed,attr"` Name Name - VideoSourceConfiguration VideoSourceConfiguration - AudioSourceConfiguration AudioSourceConfiguration - VideoEncoderConfiguration VideoEncoderConfiguration - AudioEncoderConfiguration AudioEncoderConfiguration - VideoAnalyticsConfiguration VideoAnalyticsConfiguration - PTZConfiguration PTZConfiguration - MetadataConfiguration MetadataConfiguration - Extension ProfileExtension + VideoSourceConfiguration *VideoSourceConfiguration `xml:",omitempty"` + AudioSourceConfiguration *AudioSourceConfiguration `xml:",omitempty"` + VideoEncoderConfiguration *VideoEncoderConfiguration `xml:",omitempty"` + AudioEncoderConfiguration *AudioEncoderConfiguration `xml:",omitempty"` + VideoAnalyticsConfiguration *VideoAnalyticsConfiguration `xml:",omitempty"` + PTZConfiguration *PTZConfiguration `xml:",omitempty"` + MetadataConfiguration *MetadataConfiguration `xml:",omitempty"` + Extension *ProfileExtension `xml:",omitempty"` } type VideoSourceConfiguration struct { ConfigurationEntity - ViewMode string `xml:"ViewMode,attr"` - SourceToken ReferenceToken `xml:"onvif:SourceToken"` - Bounds IntRectangle `xml:"onvif:Bounds"` - Extension VideoSourceConfigurationExtension `xml:"onvif:Extension"` + ViewMode string `xml:"ViewMode,attr"` + SourceToken *ReferenceToken `xml:",omitempty"` + Bounds *IntRectangle `xml:",omitempty"` + Extension *VideoSourceConfigurationExtension `xml:",omitempty"` } type ConfigurationEntity struct { - Token ReferenceToken `xml:"token,attr"` - Name Name `xml:"onvif:Name"` - UseCount int `xml:"onvif:UseCount"` + Token ReferenceToken `json:",omitempty" xml:"token,attr,omitempty"` + Name Name `json:",omitempty" xml:",omitempty"` + UseCount int `json:",omitempty" xml:",omitempty"` +} + +type ConfigurationEntityRequest struct { + Token ReferenceToken `xml:"token,attr,omitempty"` + Name Name `xml:"onvif:Name,omitempty"` + UseCount int `xml:"onvif:UseCount,omitempty"` } type VideoSourceConfigurationExtension struct { - Rotate Rotate `xml:"onvif:Rotate"` - Extension VideoSourceConfigurationExtension2 `xml:"onvif:Extension"` + Rotate *Rotate `xml:",omitempty"` + Extension *VideoSourceConfigurationExtension2 `xml:",omitempty"` } type Rotate struct { - Mode RotateMode `xml:"onvif:Mode"` - Degree xsd.Int `xml:"onvif:Degree"` - Extension RotateExtension `xml:"onvif:Extension"` + Mode RotateMode `xml:"Mode"` + Degree xsd.Int `xml:"Degree"` + Extension RotateExtension `xml:"Extension"` } type RotateMode xsd.String @@ -385,15 +396,15 @@ type RotateMode xsd.String type RotateExtension xsd.AnyType type VideoSourceConfigurationExtension2 struct { - LensDescription LensDescription `xml:"onvif:LensDescription"` - SceneOrientation SceneOrientation `xml:"onvif:SceneOrientation"` + LensDescription LensDescription `xml:"LensDescription"` + SceneOrientation SceneOrientation `xml:"SceneOrientation"` } type LensDescription struct { FocalLength float64 `xml:"FocalLength,attr"` - Offset LensOffset `xml:"onvif:Offset"` - Projection LensProjection `xml:"onvif:Projection"` - XFactor float64 `xml:"onvif:XFactor"` + Offset LensOffset `xml:"Offset"` + Projection LensProjection `xml:"Projection"` + XFactor float64 `xml:"XFactor"` } type LensOffset struct { @@ -402,68 +413,109 @@ type LensOffset struct { } type LensProjection struct { - Angle float64 `xml:"onvif:Angle"` - Radius float64 `xml:"onvif:Radius"` - Transmittance float64 `xml:"onvif:Transmittance"` + Angle float64 `xml:"Angle"` + Radius float64 `xml:"Radius"` + Transmittance float64 `xml:"Transmittance"` } type SceneOrientation struct { - Mode SceneOrientationMode `xml:"onvif:Mode"` - Orientation xsd.String `xml:"onvif:Orientation"` + Mode SceneOrientationMode `xml:"Mode"` + Orientation xsd.String `xml:"Orientation"` } type SceneOrientationMode xsd.String type AudioSourceConfiguration struct { ConfigurationEntity - SourceToken ReferenceToken `xml:"onvif:SourceToken"` + SourceToken ReferenceToken `xml:"SourceToken"` } type VideoEncoderConfiguration struct { ConfigurationEntity - Encoding VideoEncoding `xml:"onvif:Encoding"` - Resolution VideoResolution `xml:"onvif:Resolution"` - Quality float64 `xml:"onvif:Quality"` - RateControl VideoRateControl `xml:"onvif:RateControl"` - MPEG4 Mpeg4Configuration `xml:"onvif:MPEG4"` - H264 H264Configuration `xml:"onvif:H264"` - Multicast MulticastConfiguration `xml:"onvif:Multicast"` - SessionTimeout xsd.Duration `xml:"onvif:SessionTimeout"` + Encoding *VideoEncoding `json:",omitempty"` + Resolution *VideoResolution `json:",omitempty"` + Quality float64 `json:",omitempty"` + RateControl *VideoRateControl `json:",omitempty"` + MPEG4 *Mpeg4Configuration `json:",omitempty"` + H264 *H264Configuration `json:",omitempty"` + Multicast *MulticastConfiguration `json:",omitempty"` + SessionTimeout *xsd.Duration `json:",omitempty"` +} + +type VideoEncoderConfigurationRequest struct { + ConfigurationEntityRequest + Encoding *VideoEncoding `xml:"onvif:Encoding,omitempty"` + Resolution *VideoResolutionRequest `xml:"onvif:Resolution,omitempty"` + Quality *xsd.Float `xml:"onvif:Quality,omitempty"` + RateControl *VideoRateControlRequest `xml:"onvif:RateControl,omitempty"` + MPEG4 *Mpeg4ConfigurationRequest `xml:"onvif:MPEG4,omitempty"` + H264 *H264ConfigurationRequest `xml:"onvif:H264,omitempty"` + Multicast *MulticastConfigurationRequest `xml:"onvif:Multicast,omitempty"` + SessionTimeout *xsd.Duration `xml:"onvif:SessionTimeout,omitempty"` } type VideoEncoding xsd.String type VideoRateControl struct { - FrameRateLimit xsd.Int `xml:"onvif:FrameRateLimit"` - EncodingInterval xsd.Int `xml:"onvif:EncodingInterval"` - BitrateLimit xsd.Int `xml:"onvif:BitrateLimit"` + FrameRateLimit *xsd.Int `json:",omitempty"` + EncodingInterval *xsd.Int `json:",omitempty"` + BitrateLimit *xsd.Int `json:",omitempty"` +} + +type VideoRateControlRequest struct { + FrameRateLimit *xsd.Int `xml:"onvif:FrameRateLimit,omitempty"` + EncodingInterval *xsd.Int `xml:"onvif:EncodingInterval,omitempty"` + BitrateLimit *xsd.Int `xml:"onvif:BitrateLimit,omitempty"` } type Mpeg4Configuration struct { - GovLength xsd.Int `xml:"onvif:GovLength"` - Mpeg4Profile Mpeg4Profile `xml:"onvif:Mpeg4Profile"` + GovLength *xsd.Int `json:",omitempty"` + Mpeg4Profile *Mpeg4Profile `json:",omitempty"` +} + +type Mpeg4ConfigurationRequest struct { + GovLength *xsd.Int `xml:"onvif:GovLength,omitempty"` + Mpeg4Profile *Mpeg4Profile `xml:"onvif:Mpeg4Profile,omitempty"` } type Mpeg4Profile xsd.String type H264Configuration struct { - GovLength xsd.Int `xml:"onvif:GovLength"` - H264Profile H264Profile `xml:"onvif:H264Profile"` + GovLength *xsd.Int `json:",omitempty"` + H264Profile *H264Profile `json:",omitempty"` +} + +type H264ConfigurationRequest struct { + GovLength *xsd.Int `xml:"onvif:GovLength,omitempty"` + H264Profile *H264Profile `xml:"onvif:H264Profile,omitempty"` } type H264Profile xsd.String type MulticastConfiguration struct { - Address IPAddress `xml:"onvif:Address"` - Port int `xml:"onvif:Port"` - TTL int `xml:"onvif:TTL"` - AutoStart xsd.Boolean `xml:"onvif:AutoStart"` + Address *IPAddress `json:",omitempty"` + Port *xsd.Int `json:",omitempty"` + TTL *xsd.Int `json:",omitempty"` + AutoStart *xsd.Boolean `json:",omitempty"` +} + +type MulticastConfigurationRequest struct { + Address *IPAddressRequest `xml:"onvif:Address,omitempty"` + Port *xsd.Int `xml:"onvif:Port,omitempty"` + TTL *xsd.Int `xml:"onvif:TTL,omitempty"` + AutoStart *xsd.Boolean `xml:"onvif:AutoStart,omitempty"` } type IPAddress struct { - Type IPType `xml:"onvif:Type"` - IPv4Address IPv4Address `xml:"onvif:IPv4Address"` - IPv6Address IPv6Address `xml:"onvif:IPv6Address"` + Type IPType `json:",omitempty"` + IPv4Address IPv4Address `json:",omitempty"` + IPv6Address IPv6Address `json:",omitempty"` +} + +type IPAddressRequest struct { + Type IPType `xml:"onvif:Type,omitempty"` + IPv4Address IPv4Address `xml:"onvif:IPv4Address,omitempty"` + IPv6Address IPv6Address `xml:"onvif:IPv6Address,omitempty"` } type IPType xsd.String @@ -476,45 +528,93 @@ type IPv6Address xsd.Token type AudioEncoderConfiguration struct { ConfigurationEntity - Encoding AudioEncoding `xml:"onvif:Encoding"` - Bitrate int `xml:"onvif:Bitrate"` - SampleRate int `xml:"onvif:SampleRate"` - Multicast MulticastConfiguration `xml:"onvif:Multicast"` - SessionTimeout xsd.Duration `xml:"onvif:SessionTimeout"` + Encoding AudioEncoding `xml:"Encoding"` + Bitrate int `xml:"Bitrate"` + SampleRate int `xml:"SampleRate"` + Multicast MulticastConfiguration `xml:"Multicast"` + SessionTimeout xsd.Duration `xml:"SessionTimeout"` } type AudioEncoding xsd.String type VideoAnalyticsConfiguration struct { ConfigurationEntity - AnalyticsEngineConfiguration AnalyticsEngineConfiguration `xml:"onvif:AnalyticsEngineConfiguration"` - RuleEngineConfiguration RuleEngineConfiguration `xml:"onvif:RuleEngineConfiguration"` + AnalyticsEngineConfiguration *AnalyticsEngineConfiguration `xml:"AnalyticsEngineConfiguration"` + RuleEngineConfiguration *RuleEngineConfiguration `xml:"RuleEngineConfiguration"` } type AnalyticsEngineConfiguration struct { - AnalyticsModule Config `xml:"onvif:AnalyticsModule"` - Extension AnalyticsEngineConfigurationExtension `xml:"onvif:Extension"` + AnalyticsModule []AnalyticsModule `json:",omitempty"` + Extension *AnalyticsEngineConfigurationExtension `json:",omitempty"` +} + +type AnalyticsModule struct { + Name string `xml:",attr"` + Type string `xml:",attr"` + Parameters Parameters +} + +type Parameters struct { + SimpleItem []SimpleItem `json:",omitempty"` + ElementItem []ElementItem `json:",omitempty"` +} + +type AnalyticsEngineConfigurationRequest struct { + AnalyticsModule *ConfigRequest `xml:"onvif:AnalyticsEngineConfigurationRequest,omitempty"` + Extension *AnalyticsEngineConfigurationExtension `xml:"onvif:Extension,omitempty"` } type Config struct { - Name string `xml:"Name,attr"` - Type xsd.QName `xml:"Type,attr"` - Parameters ItemList `xml:"onvif:Parameters"` + Name string `json:",omitempty" xml:",attr"` + Type *xsd.QName `json:",omitempty" xml:",attr"` + Parameters *ItemList `json:",omitempty"` } type ItemList struct { - SimpleItem SimpleItem `xml:"onvif:SimpleItem"` - ElementItem ElementItem `xml:"onvif:ElementItem"` - Extension ItemListExtension `xml:"onvif:Extension"` + SimpleItem []SimpleItem `json:",omitempty"` + ElementItem []ElementItem `json:",omitempty"` + Extension *ItemListExtension `json:",omitempty"` } type SimpleItem struct { - Name string `xml:"Name,attr"` - Value xsd.AnySimpleType `xml:"Value,attr"` + Name *xsd.String `json:",omitempty" xml:",attr"` + Value *xsd.String `json:",omitempty" xml:",attr"` } type ElementItem struct { - Name string `xml:"Name,attr"` + Name *xsd.String `json:",omitempty" xml:",attr"` + Value *xsd.String `json:",omitempty" xml:",attr"` +} + +type ConfigRequest struct { + Name string `xml:",attr,omitempty"` + Type *xsd.QName `xml:",attr,omitempty"` + Parameters *ItemListRequest `xml:"onvif:Parameters,omitempty"` +} + +type ItemListRequest struct { + SimpleItem []SimpleItemRequest `xml:"onvif:SimpleItem,omitempty"` + ElementItem []ElementItemRequest `xml:"onvif:ElementItem,omitempty"` + Extension *ItemListExtension `xml:"onvif:Extension,omitempty"` +} + +type ElementItemRequest struct { + Name string `xml:",attr,omitempty"` + Polyline *Polyline `xml:"onvif:Polyline,omitempty"` +} + +type Polyline struct { + Point []Point `xml:"onvif:Point,omitempty"` +} + +type Point struct { + X *xsd.String `xml:"x,attr,omitempty"` + Y *xsd.String `xml:"onvif:y,attr,omitempty"` +} + +type SimpleItemRequest struct { + Name string `xml:",attr,omitempty"` + Value xsd.AnySimpleType `xml:",attr,omitempty"` } type ItemListExtension xsd.AnyType @@ -522,29 +622,36 @@ type ItemListExtension xsd.AnyType type AnalyticsEngineConfigurationExtension xsd.AnyType type RuleEngineConfiguration struct { - Rule Config `xml:"onvif:Rule"` - Extension RuleEngineConfigurationExtension `xml:"onvif:Extension"` + Rule *Config `json:",omitempty"` + Extension *RuleEngineConfigurationExtension `json:",omitempty"` } type RuleEngineConfigurationExtension xsd.AnyType type PTZConfiguration struct { - ConfigurationEntity - MoveRamp int `xml:"MoveRamp,attr"` - PresetRamp int `xml:"PresetRamp,attr"` - PresetTourRamp int `xml:"PresetTourRamp,attr"` - NodeToken ReferenceToken `xml:"NodeToken"` - DefaultAbsolutePantTiltPositionSpace xsd.AnyURI `xml:"DefaultAbsolutePantTiltPositionSpace"` - DefaultAbsoluteZoomPositionSpace xsd.AnyURI `xml:"DefaultAbsoluteZoomPositionSpace"` - DefaultRelativePanTiltTranslationSpace xsd.AnyURI `xml:"DefaultRelativePanTiltTranslationSpace"` - DefaultRelativeZoomTranslationSpace xsd.AnyURI `xml:"DefaultRelativeZoomTranslationSpace"` - DefaultContinuousPanTiltVelocitySpace xsd.AnyURI `xml:"DefaultContinuousPanTiltVelocitySpace"` - DefaultContinuousZoomVelocitySpace xsd.AnyURI `xml:"DefaultContinuousZoomVelocitySpace"` - DefaultPTZSpeed PTZSpeed `xml:"DefaultPTZSpeed"` - DefaultPTZTimeout xsd.Duration `xml:"DefaultPTZTimeout"` - PanTiltLimits PanTiltLimits `xml:"PanTiltLimits"` - ZoomLimits ZoomLimits `xml:"ZoomLimits"` - Extension PTZConfigurationExtension `xml:"Extension"` + PTZConfigurationEntity + Token ReferenceToken `xml:"token,attr"` + MoveRamp int `json:",omitempty" xml:"MoveRamp,attr,omitempty"` + PresetRamp int `json:",omitempty" xml:"PresetRamp,attr,omitempty"` + PresetTourRamp int `json:",omitempty" xml:"PresetTourRamp,attr,omitempty"` + NodeToken *ReferenceToken `json:",omitempty" xml:"tptz:NodeToken,omitempty"` + DefaultAbsolutePantTiltPositionSpace *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + DefaultAbsoluteZoomPositionSpace *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + DefaultRelativePanTiltTranslationSpace *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + DefaultRelativeZoomTranslationSpace *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + DefaultContinuousPanTiltVelocitySpace *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + DefaultContinuousZoomVelocitySpace *xsd.AnyURI `json:",omitempty" xml:",omitempty"` + DefaultPTZSpeed *PTZSpeed `json:",omitempty" xml:",omitempty"` + DefaultPTZTimeout *xsd.Duration `json:",omitempty" xml:",omitempty"` + PanTiltLimits *PanTiltLimits `json:",omitempty" xml:",omitempty"` + ZoomLimits *ZoomLimits `json:",omitempty" xml:",omitempty"` + Extension *PTZConfigurationExtension `json:",omitempty" xml:",omitempty"` +} + +type PTZConfigurationEntity struct { + Token ReferenceToken `json:",omitempty" xml:"token,attr,omitempty"` + Name Name `json:",omitempty" xml:"tptz:Name,omitempty"` + UseCount int `json:",omitempty" xml:"tptz:UseCount,omitempty"` } type PTZSpeed interface { @@ -559,24 +666,24 @@ type PTZSpeedPanTilt struct { } type Vector2D struct { - X float64 `xml:"x,attr"` - Y float64 `xml:"y,attr"` - Space xsd.AnyURI `xml:"space,attr,omitempty"` + X float64 `xml:"x,attr,omitempty"` + Y float64 `xml:"y,attr,omitempty"` + Space *xsd.AnyURI `xml:"space,attr,omitempty"` } type Vector1D struct { - X float64 `xml:"x,attr"` - Space xsd.AnyURI `xml:"space,attr,omitempty"` + X float64 `xml:"x,attr,omitempty"` + Space *xsd.AnyURI `xml:"space,attr,omitempty"` } type PanTiltLimits struct { - Range Space2DDescription `xml:"Range"` + Range *Space2DDescription `xml:"Range,omitempty"` } type Space2DDescription struct { - URI xsd.AnyURI `xml:"URI"` - XRange FloatRange `xml:"XRange"` - YRange FloatRange `xml:"YRange"` + URI *xsd.AnyURI `xml:"URI,omitempty"` + XRange *FloatRange `xml:"XRange,omitempty"` + YRange *FloatRange `xml:"YRange,omitempty"` } type ZoomLimits struct { @@ -589,24 +696,24 @@ type Space1DDescription struct { } type PTZConfigurationExtension struct { - PTControlDirection PTControlDirection `xml:"onvif:PTControlDirection"` - Extension PTZConfigurationExtension2 `xml:"onvif:Extension"` + PTControlDirection *PTControlDirection `xml:"PTControlDirection,omitempty"` + Extension *PTZConfigurationExtension2 `xml:"Extension,omitempty"` } type PTControlDirection struct { - EFlip EFlip `xml:"onvif:EFlip"` - Reverse Reverse `xml:"onvif:Reverse"` - Extension PTControlDirectionExtension `xml:"onvif:Extension"` + EFlip EFlip `xml:"EFlip"` + Reverse Reverse `xml:"Reverse"` + Extension PTControlDirectionExtension `xml:"Extension"` } type EFlip struct { - Mode EFlipMode `xml:"onvif:Mode"` + Mode EFlipMode `xml:"Mode"` } type EFlipMode xsd.String type Reverse struct { - Mode ReverseMode `xml:"onvif:Mode"` + Mode ReverseMode `xml:"Mode"` } type ReverseMode xsd.String @@ -617,24 +724,46 @@ type PTZConfigurationExtension2 xsd.AnyType type MetadataConfiguration struct { ConfigurationEntity - CompressionType string `xml:"CompressionType,attr"` - PTZStatus PTZFilter `xml:"onvif:PTZStatus"` - Events EventSubscription `xml:"onvif:Events"` - Analytics xsd.Boolean `xml:"onvif:Analytics"` - Multicast MulticastConfiguration `xml:"onvif:Multicast"` - SessionTimeout xsd.Duration `xml:"onvif:SessionTimeout"` - AnalyticsEngineConfiguration AnalyticsEngineConfiguration `xml:"onvif:AnalyticsEngineConfiguration"` - Extension MetadataConfigurationExtension `xml:"onvif:Extension"` + CompressionType string `json:",omitempty" xml:",attr,omitempty"` + PTZStatus *PTZFilter `json:",omitempty" xml:",omitempty"` + Events *EventSubscription `json:",omitempty" xml:",omitempty"` + Analytics *xsd.Boolean `json:",omitempty" xml:",omitempty"` + Multicast *MulticastConfiguration `json:",omitempty" xml:",omitempty"` + SessionTimeout *xsd.Duration `json:",omitempty" xml:",omitempty"` + AnalyticsEngineConfiguration *AnalyticsEngineConfiguration `json:",omitempty" xml:",omitempty"` + Extension *MetadataConfigurationExtension `json:",omitempty" xml:",omitempty"` +} + +type MetadataConfigurationRequest struct { + ConfigurationEntity + CompressionType string `xml:"onvif:CompressionType,attr,omitempty"` + PTZStatus *PTZFilterRequest `xml:"onvif:PTZStatus,omitempty"` + Events *EventSubscriptionRequest `xml:"onvif:Events,omitempty"` + Analytics *xsd.Boolean `xml:"onvif:Analytics,omitempty"` + Multicast *MulticastConfigurationRequest `xml:"onvif:Multicast,omitempty"` + SessionTimeout *xsd.Duration `xml:"onvif:CompressionType,omitempty"` + AnalyticsEngineConfiguration *AnalyticsEngineConfigurationRequest `xml:"onvif:AnalyticsEngineConfiguration,omitempty"` + Extension *MetadataConfigurationExtension `xml:"onvif:Extension,omitempty"` } type PTZFilter struct { - Status bool `xml:"onvif:Status"` - Position bool `xml:"onvif:Position"` + Status bool `xml:"Status"` + Position bool `xml:"Position"` +} + +type PTZFilterRequest struct { + Status bool `xml:"onvif:Status,omitempty"` + Position bool `xml:"onvif:Position,omitempty"` } type EventSubscription struct { - Filter FilterType `xml:"onvif:Filter"` - SubscriptionPolicy `xml:"onvif:SubscriptionPolicy"` + Filter *FilterType `json:",omitempty"` + SubscriptionPolicy *SubscriptionPolicy `json:",omitempty"` +} + +type EventSubscriptionRequest struct { + Filter FilterType `xml:"onvif:Filter,omitempty"` + SubscriptionPolicy SubscriptionPolicy `xml:"onvif:SubscriptionPolicy,omitempty"` } type FilterType xsd.AnyType @@ -644,16 +773,16 @@ type SubscriptionPolicy xsd.AnyType type MetadataConfigurationExtension xsd.AnyType type ProfileExtension struct { - AudioOutputConfiguration AudioOutputConfiguration - AudioDecoderConfiguration AudioDecoderConfiguration - Extension ProfileExtension2 + AudioOutputConfiguration *AudioOutputConfiguration `xml:",omitempty"` + AudioDecoderConfiguration *AudioDecoderConfiguration `xml:",omitempty"` + Extension *ProfileExtension2 `xml:",omitempty"` } type AudioOutputConfiguration struct { ConfigurationEntity - OutputToken ReferenceToken `xml:"onvif:OutputToken"` - SendPrimacy xsd.AnyURI `xml:"onvif:SendPrimacy"` - OutputLevel int `xml:"onvif:OutputLevel"` + OutputToken ReferenceToken `xml:"OutputToken"` + SendPrimacy xsd.AnyURI `xml:"SendPrimacy"` + OutputLevel int `xml:"OutputLevel"` } type AudioDecoderConfiguration struct { @@ -691,15 +820,15 @@ type VideoSourceConfigurationOptionsExtension2 struct { } type VideoEncoderConfigurationOptions struct { - QualityRange IntRange - JPEG JpegOptions - MPEG4 Mpeg4Options - H264 H264Options - Extension VideoEncoderOptionsExtension + QualityRange *IntRange `json:",omitempty"` + JPEG *JpegOptions `json:",omitempty"` + MPEG4 *Mpeg4Options `json:",omitempty"` + H264 *H264Options `json:",omitempty"` + Extension *VideoEncoderOptionsExtension `json:",omitempty"` } type JpegOptions struct { - ResolutionsAvailable VideoResolution + ResolutionsAvailable []VideoResolution FrameRateRange IntRange EncodingIntervalRange IntRange } @@ -713,18 +842,18 @@ type Mpeg4Options struct { } type H264Options struct { - ResolutionsAvailable VideoResolution + ResolutionsAvailable []VideoResolution GovLengthRange IntRange FrameRateRange IntRange EncodingIntervalRange IntRange - H264ProfilesSupported H264Profile + H264ProfilesSupported []H264Profile } type VideoEncoderOptionsExtension struct { - JPEG JpegOptions2 - MPEG4 Mpeg4Options2 - H264 H264Options2 - Extension VideoEncoderOptionsExtension2 + JPEG *JpegOptions2 `json:",omitempty"` + MPEG4 *Mpeg4Options2 `json:",omitempty"` + H264 *H264Options2 `json:",omitempty"` + Extension *VideoEncoderOptionsExtension2 `json:",omitempty"` } type JpegOptions2 struct { @@ -762,8 +891,8 @@ type AudioEncoderConfigurationOption struct { } type MetadataConfigurationOptions struct { - PTZStatusFilterOptions PTZStatusFilterOptions - Extension MetadataConfigurationOptionsExtension + PTZStatusFilterOptions *PTZStatusFilterOptions `json:",omitempty" xml:",omitempty"` + Extension *MetadataConfigurationOptionsExtension `json:",omitempty" xml:",omitempty"` } type PTZStatusFilterOptions struct { @@ -771,14 +900,14 @@ type PTZStatusFilterOptions struct { ZoomStatusSupported bool PanTiltPositionSupported bool ZoomPositionSupported bool - Extension PTZStatusFilterOptionsExtension + Extension *PTZStatusFilterOptionsExtension `json:",omitempty" xml:",omitempty"` } type PTZStatusFilterOptionsExtension xsd.AnyType type MetadataConfigurationOptionsExtension struct { - CompressionType string - Extension MetadataConfigurationOptionsExtension2 + CompressionType string `json:",omitempty" xml:",omitempty"` + Extension *MetadataConfigurationOptionsExtension2 `json:",omitempty" xml:",omitempty"` } type MetadataConfigurationOptionsExtension2 xsd.AnyType @@ -814,15 +943,15 @@ type G726DecOptions struct { type AudioDecoderConfigurationOptionsExtension xsd.AnyType type StreamSetup struct { - Stream StreamType `xml:"onvif:Stream"` - Transport Transport `xml:"onvif:Transport"` + Stream *StreamType `xml:"onvif:Stream,omitempty"` + Transport *Transport `xml:"onvif:Transport,omitempty"` } type StreamType xsd.String type Transport struct { - Protocol TransportProtocol `xml:"onvif:Protocol"` - Tunnel *Transport `xml:"onvif:Tunnel"` + Protocol *TransportProtocol `xml:"onvif:Protocol,omitempty"` + Tunnel *Transport `xml:"onvif:Tunnel,omitempty"` } // enum @@ -916,9 +1045,7 @@ type OSDImgOptions struct { Extension OSDImgOptionsExtension } -type StringAttrList struct { - AttrList []string -} +type StringAttrList []string type OSDImgOptionsExtension xsd.AnyType @@ -928,14 +1055,14 @@ type OSDConfigurationOptionsExtension xsd.AnyType type PTZNode struct { DeviceEntity - FixedHomePosition xsd.Boolean `xml:"FixedHomePosition,attr"` - GeoMove xsd.Boolean `xml:"GeoMove,attr"` - Name Name - SupportedPTZSpaces PTZSpaces - MaximumNumberOfPresets int - HomeSupported xsd.Boolean - AuxiliaryCommands AuxiliaryData - Extension PTZNodeExtension + FixedHomePosition *xsd.Boolean `json:",omitempty" xml:",attr,omitempty"` + GeoMove *xsd.Boolean `json:",omitempty" xml:",attr,omitempty"` + Name *Name `json:",omitempty" xml:",omitempty"` + SupportedPTZSpaces *PTZSpaces `json:",omitempty" xml:",omitempty"` + MaximumNumberOfPresets int `json:",omitempty" xml:",omitempty"` + HomeSupported *xsd.Boolean `json:",omitempty" xml:",omitempty"` + AuxiliaryCommands *AuxiliaryData `json:",omitempty" xml:",omitempty"` + Extension *PTZNodeExtension `json:",omitempty" xml:",omitempty"` } type PTZSpaces struct { @@ -972,16 +1099,14 @@ type PTZPresetTourSupportedExtension xsd.AnyType type PTZNodeExtension2 xsd.AnyType type PTZConfigurationOptions struct { - PTZRamps IntAttrList `xml:"PTZRamps,attr"` - Spaces PTZSpaces - PTZTimeout DurationRange - PTControlDirection PTControlDirectionOptions - Extension PTZConfigurationOptions2 + PTZRamps *IntAttrList `json:",omitempty" xml:",attr,omitempty"` + Spaces *PTZSpaces `json:",omitempty" xml:",omitempty"` + PTZTimeout *DurationRange `json:",omitempty" xml:",omitempty"` + PTControlDirection *PTControlDirectionOptions `json:",omitempty" xml:",omitempty"` + Extension *PTZConfigurationOptions2 `json:",omitempty" xml:",omitempty"` } -type IntAttrList struct { - IntAttrList []int -} +type IntAttrList []int type DurationRange struct { Min xsd.Duration @@ -1019,20 +1144,20 @@ type PTZPreset struct { } type PTZVector struct { - PanTilt Vector2D `xml:"PanTilt,omitempty"` - Zoom Vector1D `xml:"Zoom,omitempty"` + PanTilt *Vector2D `json:",omitempty" xml:"PanTilt,omitempty"` + Zoom *Vector1D `json:",omitempty" xml:"Zoom,omitempty"` } type PTZStatus struct { - Position PTZVector `xml:"Position"` - MoveStatus PTZMoveStatus `xml:"MoveStatus"` - Error string `xml:"Error"` - UtcTime xsd.DateTime `xml:"UtcTime"` + Position PTZVector `json:",omitempty" xml:",omitempty"` + MoveStatus PTZMoveStatus `json:",omitempty" xml:",omitempty"` + Error string `json:",omitempty" xml:",omitempty"` + UtcTime string `json:",omitempty" xml:",omitempty"` } type PTZMoveStatus struct { - PanTilt MoveStatus - Zoom MoveStatus + PanTilt string `json:",omitempty" xml:",omitempty"` + Zoom string `json:",omitempty" xml:",omitempty"` } type MoveStatus struct { @@ -1047,34 +1172,34 @@ type GeoLocation struct { type PresetTour struct { Token ReferenceToken `xml:"token,attr"` - Name Name `xml:"onvif:Name"` - Status PTZPresetTourStatus `xml:"onvif:Status"` - AutoStart xsd.Boolean `xml:"onvif:AutoStart"` - StartingCondition PTZPresetTourStartingCondition `xml:"onvif:StartingCondition"` - TourSpot PTZPresetTourSpot `xml:"onvif:TourSpot"` - Extension PTZPresetTourExtension `xml:"onvif:Extension"` + Name Name `xml:"Name"` + Status PTZPresetTourStatus `xml:"Status"` + AutoStart xsd.Boolean `xml:"AutoStart"` + StartingCondition PTZPresetTourStartingCondition `xml:"StartingCondition"` + TourSpot PTZPresetTourSpot `xml:"TourSpot"` + Extension PTZPresetTourExtension `xml:"Extension"` } type PTZPresetTourStatus struct { - State PTZPresetTourState `xml:"onvif:State"` - CurrentTourSpot PTZPresetTourSpot `xml:"onvif:CurrentTourSpot"` - Extension PTZPresetTourStatusExtension `xml:"onvif:Extension"` + State PTZPresetTourState `xml:"State"` + CurrentTourSpot PTZPresetTourSpot `xml:"CurrentTourSpot"` + Extension PTZPresetTourStatusExtension `xml:"Extension"` } type PTZPresetTourState xsd.String type PTZPresetTourSpot struct { - PresetDetail PTZPresetTourPresetDetail `xml:"onvif:PresetDetail"` - Speed PTZSpeed `xml:"onvif:Speed"` - StayTime xsd.Duration `xml:"onvif:StayTime"` - Extension PTZPresetTourSpotExtension `xml:"onvif:Extension"` + PresetDetail PTZPresetTourPresetDetail `xml:"PresetDetail"` + Speed PTZSpeed `xml:"Speed"` + StayTime xsd.Duration `xml:"StayTime"` + Extension PTZPresetTourSpotExtension `xml:"Extension"` } type PTZPresetTourPresetDetail struct { - PresetToken ReferenceToken `xml:"onvif:PresetToken"` - Home xsd.Boolean `xml:"onvif:Home"` - PTZPosition PTZVector `xml:"onvif:PTZPosition"` - TypeExtension PTZPresetTourTypeExtension `xml:"onvif:TypeExtension"` + PresetToken ReferenceToken `xml:"PresetToken"` + Home xsd.Boolean `xml:"Home"` + PTZPosition PTZVector `xml:"PTZPosition"` + TypeExtension PTZPresetTourTypeExtension `xml:"TypeExtension"` } type PTZPresetTourTypeExtension xsd.AnyType @@ -1085,10 +1210,10 @@ type PTZPresetTourStatusExtension xsd.AnyType type PTZPresetTourStartingCondition struct { RandomPresetOrder xsd.Boolean `xml:"RandomPresetOrder,attr"` - RecurringTime xsd.Int `xml:"onvif:RecurringTime"` - RecurringDuration xsd.Duration `xml:"onvif:RecurringDuration"` - Direction PTZPresetTourDirection `xml:"onvif:Direction"` - Extension PTZPresetTourStartingConditionExtension `xml:"onvif:Extension"` + RecurringTime xsd.Int `xml:"RecurringTime"` + RecurringDuration xsd.Duration `xml:"RecurringDuration"` + Direction PTZPresetTourDirection `xml:"Direction"` + Extension PTZPresetTourStartingConditionExtension `xml:"Extension"` } type PTZPresetTourDirection xsd.String @@ -1137,7 +1262,7 @@ type OnvifVersion struct { type SetDateTimeType xsd.String type TimeZone struct { - TZ xsd.Token `xml:"onvif:TZ"` + TZ xsd.Token `xml:"TZ"` } type SystemDateTime struct { @@ -1163,8 +1288,8 @@ type Include struct { } type BackupFile struct { - Name string `xml:"onvif:Name"` - Data AttachmentData `xml:"onvif:Data"` + Name string `xml:"Name"` + Data AttachmentData `xml:"Data"` } type SystemLogType xsd.String @@ -1189,11 +1314,11 @@ type ScopeDefinition xsd.String type DiscoveryMode xsd.String type NetworkHost struct { - Type NetworkHostType `xml:"onvif:Type"` - IPv4Address IPv4Address `xml:"onvif:IPv4Address"` - IPv6Address IPv6Address `xml:"onvif:IPv6Address"` - DNSname DNSName `xml:"onvif:DNSname"` - Extension NetworkHostExtension `xml:"onvif:Extension"` + Type NetworkHostType `xml:"Type"` + IPv4Address IPv4Address `xml:"IPv4Address"` + IPv6Address IPv6Address `xml:"IPv6Address"` + DNSname DNSName `xml:"DNSname"` + Extension NetworkHostExtension `xml:"Extension"` } type NetworkHostType xsd.String @@ -1201,16 +1326,23 @@ type NetworkHostType xsd.String type NetworkHostExtension xsd.String type RemoteUser struct { - Username string `xml:"onvif:Username"` - Password string `xml:"onvif:Password"` - UseDerivedPassword xsd.Boolean `xml:"onvif:UseDerivedPassword"` + Username string `xml:"Username"` + Password string `xml:"Password"` + UseDerivedPassword xsd.Boolean `xml:"UseDerivedPassword"` } type User struct { - Username string `xml:"onvif:Username"` - Password string `xml:"onvif:Password"` - UserLevel UserLevel `xml:"onvif:UserLevel"` - Extension UserExtension `xml:"onvif:Extension"` + Username string `json:",omitempty" xml:",omitempty"` + Password string `json:",omitempty" xml:",omitempty"` + UserLevel *UserLevel `json:",omitempty" xml:",omitempty"` + Extension *UserExtension `json:",omitempty" xml:",omitempty"` +} + +type UserRequest struct { + Username string `xml:"onvif:Username,omitempty"` + Password string `xml:"onvif:Password,omitempty"` + UserLevel *UserLevel `xml:"onvif:UserLevel,omitempty"` + Extension *UserExtension `xml:"onvif:Extension,omitempty"` } type UserLevel xsd.String @@ -1427,19 +1559,19 @@ type AnalyticsDeviceExtension xsd.AnyType type CapabilitiesExtension2 xsd.AnyType type HostnameInformation struct { - FromDHCP xsd.Boolean - Name xsd.Token - Extension HostnameInformationExtension + FromDHCP *xsd.Boolean `json:"FromDHCP,omitempty"` + Name *xsd.Token `json:"Name,omitempty"` + Extension *HostnameInformationExtension `json:"Extension,omitempty"` } type HostnameInformationExtension xsd.AnyType type DNSInformation struct { - FromDHCP xsd.Boolean - SearchDomain xsd.Token - DNSFromDHCP IPAddress - DNSManual IPAddress - Extension DNSInformationExtension + FromDHCP *xsd.Boolean `json:"FromDHCP,omitempty"` + SearchDomain *xsd.Token `json:"SearchDomain,omitempty"` + DNSFromDHCP *IPAddress `json:"DNSFromDHCP,omitempty"` + DNSManual *IPAddress `json:"DNSManual,omitempty"` + Extension *DNSInformationExtension `json:"Extension,omitempty"` } type DNSInformationExtension xsd.AnyType @@ -1467,18 +1599,18 @@ type DynamicDNSInformationExtension xsd.AnyType type NetworkInterface struct { DeviceEntity - Enabled xsd.Boolean - Info NetworkInterfaceInfo - Link NetworkInterfaceLink - IPv4 IPv4NetworkInterface - IPv6 IPv6NetworkInterface - Extension NetworkInterfaceExtension + Enabled *xsd.Boolean `json:",omitempty"` + Info *NetworkInterfaceInfo `json:",omitempty"` + Link *NetworkInterfaceLink `json:",omitempty"` + IPv4 *IPv4NetworkInterface `json:",omitempty"` + IPv6 *IPv6NetworkInterface `json:",omitempty"` + Extension *NetworkInterfaceExtension `json:",omitempty"` } type NetworkInterfaceInfo struct { - Name xsd.String - HwAddress HwAddress - MTU xsd.Int + Name xsd.String `json:"Name,omitempty"` + HwAddress HwAddress `json:"HwAddress,omitempty"` + MTU xsd.Int `json:"MTU,omitempty"` } type HwAddress xsd.Token @@ -1486,15 +1618,15 @@ type HwAddress xsd.Token type NetworkInterfaceLink struct { AdminSettings NetworkInterfaceConnectionSetting OperSettings NetworkInterfaceConnectionSetting - InterfaceType IANA_IfTypes `xml:"IANA-IfTypes"` + InterfaceType *IANA_IfTypes `xml:"IANA-IfTypes,omitempty" json:"IANA-IfTypes,omitempty"` } type IANA_IfTypes xsd.Int type NetworkInterfaceConnectionSetting struct { - AutoNegotiation xsd.Boolean `xml:"onvif:AutoNegotiation"` - Speed xsd.Int `xml:"onvif:Speed"` - Duplex Duplex `xml:"onvif:Duplex"` + AutoNegotiation *xsd.Boolean `xml:"onvif:AutoNegotiation,omitempty" json:"AutoNegotiation,omitempty"` + Speed *xsd.Int `xml:"onvif:Speed,omitempty" json:"Speed,omitempty"` + Duplex *Duplex `xml:"onvif:Duplex,omitempty" json:"Duplex,omitempty"` } // TODO: enum @@ -1502,35 +1634,35 @@ type Duplex xsd.String type NetworkInterfaceExtension struct { InterfaceType IANA_IfTypes - Dot3 Dot3Configuration - Dot11 Dot11Configuration + Dot3 *Dot3Configuration `xml:"Dot3,omitempty" json:"Dot3,omitempty"` + Dot11 *Dot11Configuration `xml:"Dot11,omitempty" json:"Dot11,omitempty"` Extension NetworkInterfaceExtension2 } type NetworkInterfaceExtension2 xsd.AnyType type Dot11Configuration struct { - SSID Dot11SSIDType `xml:"onvif:SSID"` - Mode Dot11StationMode `xml:"onvif:Mode"` - Alias Name `xml:"onvif:Alias"` - Priority NetworkInterfaceConfigPriority `xml:"onvif:Priority"` - Security Dot11SecurityConfiguration `xml:"onvif:Security"` + SSID Dot11SSIDType `xml:"SSID,omitempty" json:"SSID,omitempty"` + Mode Dot11StationMode `xml:"Mode,omitempty" json:"Mode,omitempty"` + Alias Name `xml:"Alias,omitempty" json:"Alias,omitempty"` + Priority NetworkInterfaceConfigPriority `xml:"Priority,omitempty" json:"Priority,omitempty"` + Security Dot11SecurityConfiguration `xml:"Security,omitempty" json:"Security,omitempty"` } type Dot11SecurityConfiguration struct { - Mode Dot11SecurityMode `xml:"onvif:Mode"` - Algorithm Dot11Cipher `xml:"onvif:Algorithm"` - PSK Dot11PSKSet `xml:"onvif:PSK"` - Dot1X ReferenceToken `xml:"onvif:Dot1X"` - Extension Dot11SecurityConfigurationExtension `xml:"onvif:Extension"` + Mode Dot11SecurityMode `xml:"Mode,omitempty" json:"Mode,omitempty"` + Algorithm Dot11Cipher `xml:"Algorithm,omitempty" json:"Algorithm,omitempty"` + PSK Dot11PSKSet `xml:"PSK,omitempty" json:"PSK,omitempty"` + Dot1X ReferenceToken `xml:"Dot1X,omitempty" json:"Dot1X,omitempty"` + Extension Dot11SecurityConfigurationExtension `xml:"Extension,omitempty" json:"Extension,omitempty"` } type Dot11SecurityConfigurationExtension xsd.AnyType type Dot11PSKSet struct { - Key Dot11PSK `xml:"onvif:Key"` - Passphrase Dot11PSKPassphrase `xml:"onvif:Passphrase"` - Extension Dot11PSKSetExtension `xml:"onvif:Extension"` + Key Dot11PSK `xml:"Key,omitempty" json:"Key,omitempty"` + Passphrase Dot11PSKPassphrase `xml:"Passphrase,omitempty" json:"Passphrase,omitempty"` + Extension Dot11PSKSetExtension `xml:"Extension,omitempty" json:"Extension,omitempty"` } type Dot11PSKSetExtension xsd.AnyType @@ -1574,66 +1706,73 @@ type IPv6Configuration struct { type IPv6ConfigurationExtension xsd.AnyType type PrefixedIPv6Address struct { - Address IPv6Address `xml:"onvif:Address"` - PrefixLength xsd.Int `xml:"onvif:PrefixLength"` + Address IPv6Address `xml:"Address,omitempty" json:"Address,omitempty"` + PrefixLength xsd.Int `xml:"PrefixLength,omitempty" json:"PrefixLength,omitempty"` } // TODO: enumeration type IPv6DHCPConfiguration xsd.String type IPv4NetworkInterface struct { - Enabled xsd.Boolean - Config IPv4Configuration + Enabled *xsd.Boolean `json:"Enabled,omitempty"` + Config *IPv4Configuration `json:"Config,omitempty"` } type IPv4Configuration struct { - Manual PrefixedIPv4Address - LinkLocal PrefixedIPv4Address - FromDHCP PrefixedIPv4Address - DHCP xsd.Boolean + Manual *PrefixedIPv4Address `json:"Manual,omitempty"` + LinkLocal *PrefixedIPv4Address `json:"LinkLocal,omitempty"` + FromDHCP *PrefixedIPv4Address `json:"FromDHCP,omitempty"` + DHCP *xsd.Boolean `json:"DHCP,omitempty"` } // optional, unbounded type PrefixedIPv4Address struct { - Address IPv4Address `xml:"onvif:Address"` - PrefixLength xsd.Int `xml:"onvif:PrefixLength"` + Address IPv4Address `xml:"Address" json:"Address,omitempty"` + PrefixLength xsd.Int `xml:"PrefixLength" json:"PrefixLength,omitempty"` } type NetworkInterfaceSetConfiguration struct { - Enabled xsd.Boolean `xml:"onvif:Enabled"` - Link NetworkInterfaceConnectionSetting `xml:"onvif:Link"` - MTU xsd.Int `xml:"onvif:MTU"` - IPv4 IPv4NetworkInterfaceSetConfiguration `xml:"onvif:IPv4"` - IPv6 IPv6NetworkInterfaceSetConfiguration `xml:"onvif:IPv6"` - Extension NetworkInterfaceSetConfigurationExtension `xml:"onvif:Extension"` + Enabled *xsd.Boolean `xml:"onvif:Enabled,omitempty"` + Link *NetworkInterfaceConnectionSetting `xml:"onvif:Link,omitempty"` + MTU *xsd.Int `xml:"onvif:MTU,omitempty"` + IPv4 *IPv4NetworkInterfaceSetConfiguration `xml:"onvif:IPv4,omitempty"` + IPv6 *IPv6NetworkInterfaceSetConfiguration `xml:"onvif:IPv6,omitempty"` + Extension *NetworkInterfaceSetConfigurationExtension `xml:"onvif:Extension,omitempty"` } type NetworkInterfaceSetConfigurationExtension struct { - Dot3 Dot3Configuration `xml:"onvif:Dot3"` - Dot11 Dot11Configuration `xml:"onvif:Dot11"` - Extension NetworkInterfaceSetConfigurationExtension2 `xml:"onvif:Extension"` + Dot3 Dot3Configuration `xml:"onvif:Dot3,omitempty"` + Dot11 Dot11Configuration `xml:"onvif:Dot11,omitempty"` + Extension NetworkInterfaceSetConfigurationExtension2 `xml:"onvif:Extension,omitempty"` } type NetworkInterfaceSetConfigurationExtension2 xsd.AnyType type IPv6NetworkInterfaceSetConfiguration struct { - Enabled xsd.Boolean `xml:"onvif:Enabled"` - AcceptRouterAdvert xsd.Boolean `xml:"onvif:AcceptRouterAdvert"` - Manual PrefixedIPv6Address `xml:"onvif:Manual"` - DHCP IPv6DHCPConfiguration `xml:"onvif:DHCP"` + Enabled *xsd.Boolean `xml:"onvif:Enabled,omitempty" json:",omitempty"` + AcceptRouterAdvert *xsd.Boolean `xml:"onvif:AcceptRouterAdvert,omitempty" json:",omitempty"` + Manual *PrefixedIPv6Address `xml:"onvif:Manual,omitempty" json:",omitempty"` + DHCP *IPv6DHCPConfiguration `xml:"onvif:DHCP,omitempty" json:",omitempty"` } type IPv4NetworkInterfaceSetConfiguration struct { - Enabled xsd.Boolean `xml:"onvif:Enabled"` - Manual PrefixedIPv4Address `xml:"onvif:Manual"` - DHCP xsd.Boolean `xml:"onvif:DHCP"` + Enabled *xsd.Boolean `xml:"onvif:Enabled,omitempty"` + Manual *PrefixedIPv4Address `xml:"onvif:Manual,omitempty"` + DHCP *xsd.Boolean `xml:"onvif:DHCP,omitempty"` } -type NetworkProtocol struct { - Name NetworkProtocolType `xml:"onvif:Name"` - Enabled xsd.Boolean `xml:"onvif:Enabled"` - Port xsd.Int `xml:"onvif:Port"` - Extension NetworkProtocolExtension `xml:"onvif:Extension"` +type NetworkProtocolResponse struct { + Name *NetworkProtocolType `json:",omitempty"` + Enabled *xsd.Boolean `json:",omitempty"` + Port *xsd.Int `json:",omitempty"` + Extension *NetworkProtocolExtension `json:",omitempty"` +} + +type NetworkProtocolRequest struct { + Name *NetworkProtocolType `xml:"onvif:Name,omitempty"` + Enabled *xsd.Boolean `xml:"onvif:Enabled,omitempty"` + Port *xsd.Int `xml:"onvif:Port,omitempty"` + Extension *NetworkProtocolExtension `xml:"onvif:Extension,omitempty"` } type NetworkProtocolExtension xsd.AnyType @@ -1642,8 +1781,8 @@ type NetworkProtocolExtension xsd.AnyType type NetworkProtocolType xsd.String type NetworkGateway struct { - IPv4Address IPv4Address - IPv6Address IPv6Address + IPv4Address *IPv4Address `json:"IPv4Address,omitempty"` + IPv6Address *IPv6Address `json:"IPv6Address,omitempty"` } type NetworkZeroConfiguration struct { @@ -1661,10 +1800,10 @@ type NetworkZeroConfigurationExtension struct { type NetworkZeroConfigurationExtension2 xsd.AnyType type IPAddressFilter struct { - Type IPAddressFilterType `xml:"onvif:Type"` - IPv4Address PrefixedIPv4Address `xml:"onvif:IPv4Address,omitempty"` - IPv6Address PrefixedIPv6Address `xml:"onvif:IPv6Address,omitempty"` - Extension IPAddressFilterExtension `xml:"onvif:Extension,omitempty"` + Type IPAddressFilterType `xml:"Type,omitempty"` + IPv4Address PrefixedIPv4Address `xml:"IPv4Address,omitempty"` + IPv6Address PrefixedIPv6Address `xml:"IPv6Address,omitempty"` + Extension IPAddressFilterExtension `xml:"Extension,omitempty"` } type IPAddressFilterExtension xsd.AnyType @@ -1676,17 +1815,17 @@ type IPAddressFilterType xsd.String // TODO: attribite type BinaryData struct { X ContentType `xml:"xmime:contentType,attr"` - Data xsd.Base64Binary `xml:"onvif:Data"` + Data xsd.Base64Binary `xml:"Data"` } type Certificate struct { - CertificateID xsd.Token `xml:"onvif:CertificateID"` - Certificate BinaryData `xml:"onvif:Certificate"` + CertificateID xsd.Token `xml:"CertificateID"` + Certificate BinaryData `xml:"Certificate"` } type CertificateStatus struct { - CertificateID xsd.Token `xml:"onvif:CertificateID"` - Status xsd.Boolean `xml:"onvif:Status"` + CertificateID xsd.Token `xml:"CertificateID"` + Status xsd.Boolean `xml:"Status"` } type RelayOutput struct { @@ -1695,9 +1834,9 @@ type RelayOutput struct { } type RelayOutputSettings struct { - Mode RelayMode `xml:"onvif:Mode"` - DelayTime xsd.Duration `xml:"onvif:DelayTime"` - IdleState RelayIdleState `xml:"onvif:IdleState"` + Mode RelayMode `xml:"Mode"` + DelayTime xsd.Duration `xml:"DelayTime"` + IdleState RelayIdleState `xml:"IdleState"` } // TODO:enumeration @@ -1710,9 +1849,9 @@ type RelayMode xsd.String type RelayLogicalState xsd.String type CertificateWithPrivateKey struct { - CertificateID xsd.Token `xml:"onvif:CertificateID"` - Certificate BinaryData `xml:"onvif:Certificate"` - PrivateKey BinaryData `xml:"onvif:PrivateKey"` + CertificateID xsd.Token `xml:"CertificateID"` + Certificate BinaryData `xml:"Certificate"` + PrivateKey BinaryData `xml:"PrivateKey"` } type CertificateInformation struct { @@ -1742,27 +1881,27 @@ type CertificateUsage struct { } type Dot1XConfiguration struct { - Dot1XConfigurationToken ReferenceToken `xml:"onvif:Dot1XConfigurationToken"` - Identity xsd.String `xml:"onvif:Identity"` - AnonymousID xsd.String `xml:"onvif:AnonymousID,omitempty"` - EAPMethod xsd.Int `xml:"onvif:EAPMethod"` - CACertificateID xsd.Token `xml:"onvif:CACertificateID,omitempty"` - EAPMethodConfiguration EAPMethodConfiguration `xml:"onvif:EAPMethodConfiguration,omitempty"` - Extension Dot1XConfigurationExtension `xml:"onvif:Extension,omitempty"` + Dot1XConfigurationToken ReferenceToken `xml:"Dot1XConfigurationToken"` + Identity xsd.String `xml:"Identity"` + AnonymousID xsd.String `xml:"AnonymousID,omitempty"` + EAPMethod xsd.Int `xml:"EAPMethod"` + CACertificateID xsd.Token `xml:"CACertificateID,omitempty"` + EAPMethodConfiguration EAPMethodConfiguration `xml:"EAPMethodConfiguration,omitempty"` + Extension Dot1XConfigurationExtension `xml:"Extension,omitempty"` } type Dot1XConfigurationExtension xsd.AnyType type EAPMethodConfiguration struct { - TLSConfiguration TLSConfiguration `xml:"onvif:TLSConfiguration,omitempty"` - Password xsd.String `xml:"onvif:Password,omitempty"` - Extension EapMethodExtension `xml:"onvif:Extension,omitempty"` + TLSConfiguration TLSConfiguration `xml:"TLSConfiguration,omitempty"` + Password xsd.String `xml:"Password,omitempty"` + Extension EapMethodExtension `xml:"Extension,omitempty"` } type EapMethodExtension xsd.AnyType type TLSConfiguration struct { - CertificateID xsd.Token `xml:"onvif:CertificateID,omitempty"` + CertificateID xsd.Token `xml:"CertificateID,omitempty"` } type Dot11Capabilities struct { @@ -1816,10 +1955,10 @@ type LocationEntity struct { GeoSource xsd.AnyURI `xml:"GeoSource,attr"` AutoGeo xsd.Boolean `xml:"AutoGeo,attr"` - GeoLocation GeoLocation `xml:"onvif:GeoLocation"` - GeoOrientation GeoOrientation `xml:"onvif:GeoOrientation"` - LocalLocation LocalLocation `xml:"onvif:LocalLocation"` - LocalOrientation LocalOrientation `xml:"onvif:LocalOrientation"` + GeoLocation GeoLocation `xml:"GeoLocation"` + GeoOrientation GeoOrientation `xml:"GeoOrientation"` + LocalLocation LocalLocation `xml:"LocalLocation"` + LocalOrientation LocalOrientation `xml:"LocalOrientation"` } type LocalOrientation struct { @@ -1841,38 +1980,55 @@ type GeoOrientation struct { } type FocusMove struct { - Absolute AbsoluteFocus `xml:"onvif:Absolute"` - Relative RelativeFocus `xml:"onvif:Relative"` - Continuous ContinuousFocus `xml:"onvif:Continuous"` + Absolute AbsoluteFocus `xml:"Absolute"` + Relative RelativeFocus `xml:"Relative"` + Continuous ContinuousFocus `xml:"Continuous"` } type ContinuousFocus struct { - Speed xsd.Float `xml:"onvif:Speed"` + Speed xsd.Float `xml:"Speed"` } type RelativeFocus struct { - Distance xsd.Float `xml:"onvif:Distance"` - Speed xsd.Float `xml:"onvif:Speed"` + Distance xsd.Float `xml:"Distance"` + Speed xsd.Float `xml:"Speed"` } type AbsoluteFocus struct { - Position xsd.Float `xml:"onvif:Position"` - Speed xsd.Float `xml:"onvif:Speed"` + Position xsd.Float `xml:"Position"` + Speed xsd.Float `xml:"Speed"` } type DateTime struct { - Time Time `xml:"onvif:Time"` - Date Date `xml:"onvif:Date"` + Time Time `xml:"Time"` + Date Date `xml:"Date"` } type Time struct { - Hour xsd.Int `xml:"onvif:Hour"` - Minute xsd.Int `xml:"onvif:Minute"` - Second xsd.Int `xml:"onvif:Second"` + Hour xsd.Int `xml:"Hour"` + Minute xsd.Int `xml:"Minute"` + Second xsd.Int `xml:"Second"` } type Date struct { - Year xsd.Int `xml:"onvif:Year"` - Month xsd.Int `xml:"onvif:Month"` - Day xsd.Int `xml:"onvif:Day"` + Year xsd.Int `xml:"Year"` + Month xsd.Int `xml:"Month"` + Day xsd.Int `xml:"Day"` +} + +type DateTimeRequest struct { + Time *TimeRequest `xml:"onvif:Time,omitempty"` + Date *DateRequest `xml:"onvif:Date,omitempty"` +} + +type TimeRequest struct { + Hour *xsd.Int `xml:"onvif:Hour,omitempty"` + Minute *xsd.Int `xml:"onvif:Minute,omitempty"` + Second *xsd.Int `xml:"onvif:Second,omitempty"` +} + +type DateRequest struct { + Year *xsd.Int `xml:"onvif:Year,omitempty"` + Month *xsd.Int `xml:"onvif:Month,omitempty"` + Day *xsd.Int `xml:"onvif:Day,omitempty"` } From ba8bcf19bbbd6a0b066aec8be6149cab5e1de98c Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Mon, 25 Dec 2023 20:57:16 +0100 Subject: [PATCH 08/53] inputs method added --- api/get_structs.go | 2 ++ device/function.go | 9 +++++++++ device/types.go | 8 ++++++++ deviceio/function.go | 9 +++++++++ deviceio/types.go | 8 ++++++++ event/types.go | 1 + mappings.go | 1 + names.go | 1 + xsd/onvif/onvif.go | 11 +++++++++++ 9 files changed, 50 insertions(+) diff --git a/api/get_structs.go b/api/get_structs.go index 9957fe4..052f7d1 100644 --- a/api/get_structs.go +++ b/api/get_structs.go @@ -201,6 +201,8 @@ func getDeviceStructByName(name string) (interface{}, error) { return &device.SetClientCertificateMode{}, nil case "GetRelayOutputs": return &device.GetRelayOutputs{}, nil + case "GetDigitalInputs": + return &device.GetDigitalInputs{}, nil case "SetRelayOutputSettings": return &device.SetRelayOutputSettings{}, nil case "SetRelayOutputState": diff --git a/device/function.go b/device/function.go index d018c68..7074d98 100644 --- a/device/function.go +++ b/device/function.go @@ -341,6 +341,15 @@ func (_ *GetRelayOutputsFunction) Response() interface{} { return &GetRelayOutputsResponse{} } +type GetDigitalInputsFunction struct{} + +func (_ *GetDigitalInputsFunction) Request() interface{} { + return &GetDigitalInputs{} +} +func (_ *GetDigitalInputsFunction) Response() interface{} { + return &GetDigitalInputsResponse{} +} + type GetRemoteDiscoveryModeFunction struct{} func (_ *GetRemoteDiscoveryModeFunction) Request() interface{} { diff --git a/device/types.go b/device/types.go index 210cd00..521dc7c 100644 --- a/device/types.go +++ b/device/types.go @@ -683,6 +683,14 @@ type GetRelayOutputsResponse struct { RelayOutputs onvif.RelayOutput } +type GetDigitalInputs struct { + XMLName string `xml:"tmd:GetDigitalInputs"` +} + +type GetDigitalInputsResponse struct { + DigitalInputs onvif.DigitalInput +} + type SetRelayOutputSettings struct { XMLName string `xml:"tds:SetRelayOutputSettings"` RelayOutputToken onvif.ReferenceToken `xml:"tds:RelayOutputToken"` diff --git a/deviceio/function.go b/deviceio/function.go index 1e97bc6..34c7eb1 100644 --- a/deviceio/function.go +++ b/deviceio/function.go @@ -341,6 +341,15 @@ func (_ *GetRelayOutputsFunction) Response() interface{} { return &GetRelayOutputsResponse{} } +type GetDigitalInputsFunction struct{} + +func (_ *GetDigitalInputsFunction) Request() interface{} { + return &GetDigitalInputs{} +} +func (_ *GetDigitalInputsFunction) Response() interface{} { + return &GetDigitalInputsResponse{} +} + type GetRemoteDiscoveryModeFunction struct{} func (_ *GetRemoteDiscoveryModeFunction) Request() interface{} { diff --git a/deviceio/types.go b/deviceio/types.go index f6a3b3a..4369589 100644 --- a/deviceio/types.go +++ b/deviceio/types.go @@ -683,6 +683,14 @@ type GetRelayOutputsResponse struct { RelayOutputs onvif.RelayOutput } +type GetDigitalInputs struct { + XMLName string `xml:"tmd:GetDigitalInputs"` +} + +type GetDigitalInputsResponse struct { + DigitalInputs onvif.DigitalInput +} + type SetRelayOutputSettings struct { XMLName string `xml:"tds:SetRelayOutputSettings"` RelayOutputToken onvif.ReferenceToken `xml:"tds:RelayOutputToken"` diff --git a/event/types.go b/event/types.go index a82e596..a980e12 100644 --- a/event/types.go +++ b/event/types.go @@ -166,6 +166,7 @@ type QueryExpression QueryExpressionType // TopicExpressionType struct for wsnt:TopicExpression type TopicExpressionType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd + Dialect xsd.String `xml:"Dialect,attr"` TopicKinds xsd.String `xml:",chardata"` } diff --git a/mappings.go b/mappings.go index 1c7576c..2f6726b 100644 --- a/mappings.go +++ b/mappings.go @@ -72,6 +72,7 @@ var DeviceFunctionMap = map[string]Function{ GetNetworkProtocols: &device.GetNetworkProtocolsFunction{}, GetPkcs10Request: &device.GetPkcs10RequestFunction{}, GetRelayOutputs: &device.GetRelayOutputsFunction{}, + GetDigitalInputs: &device.GetDigitalInputsFunction{}, GetRemoteDiscoveryMode: &device.GetRemoteDiscoveryModeFunction{}, GetRemoteUser: &device.GetRemoteUserFunction{}, GetScopes: &device.GetScopesFunction{}, diff --git a/names.go b/names.go index e3cbaea..4a31c8e 100644 --- a/names.go +++ b/names.go @@ -75,6 +75,7 @@ const ( GetNetworkProtocols = "GetNetworkProtocols" GetPkcs10Request = "GetPkcs10Request" GetRelayOutputs = "GetRelayOutputs" + GetDigitalInputs = "GetDigitalInputs" GetRemoteDiscoveryMode = "GetRemoteDiscoveryMode" GetRemoteUser = "GetRemoteUser" GetScopes = "GetScopes" diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index b973f06..adc48dd 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -1839,6 +1839,17 @@ type RelayOutputSettings struct { IdleState RelayIdleState `xml:"IdleState"` } +type DigitalInput struct { + Token ReferenceToken `xml:"token,attr"` + IdleState InputIdleState `xml:"IdleState,attr"` +} + +// TODO:enumeration +type InputToken xsd.String + +// TODO:enumeration +type InputIdleState xsd.String + // TODO:enumeration type RelayIdleState xsd.String From 6c5db5fed63f69109273dd175b53134f29102f6a Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Mon, 25 Dec 2023 21:21:12 +0100 Subject: [PATCH 09/53] disable imaging --- functionmap.go | 4 ++-- mappings.go | 5 ++--- xsd/onvif/onvif.go | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/functionmap.go b/functionmap.go index 49718a2..f6d0edb 100644 --- a/functionmap.go +++ b/functionmap.go @@ -20,8 +20,8 @@ func FunctionByServiceAndFunctionName(serviceName, functionName string) (Functio functionMap = EventFunctionMap case AnalyticsWebService: functionMap = AnalyticsFunctionMap - case ImagingWebService: - functionMap = ImagingFunctionMap + //case ImagingWebService: + // functionMap = ImagingFunctionMap case RecordingWebService: functionMap = RecordingFunctionMap default: diff --git a/mappings.go b/mappings.go index 2f6726b..7bf4eb9 100644 --- a/mappings.go +++ b/mappings.go @@ -12,7 +12,6 @@ import ( "github.com/kerberos-io/onvif/analytics" "github.com/kerberos-io/onvif/device" "github.com/kerberos-io/onvif/event" - "github.com/kerberos-io/onvif/imaging" "github.com/kerberos-io/onvif/media" "github.com/kerberos-io/onvif/media2" "github.com/kerberos-io/onvif/ptz" @@ -141,7 +140,7 @@ var EventFunctionMap = map[string]Function{ Unsubscribe: &event.UnsubscribeFunction{}, } -var ImagingFunctionMap = map[string]Function{ +/*var ImagingFunctionMap = map[string]Function{ GetCurrentPreset: &imaging.GetCurrentPresetFunction{}, GetImagingSettings: &imaging.GetImagingSettingsFunction{}, GetMoveOptions: &imaging.GetMoveOptionsFunction{}, @@ -153,7 +152,7 @@ var ImagingFunctionMap = map[string]Function{ SetCurrentPreset: &imaging.SetCurrentPresetFunction{}, SetImagingSettings: &imaging.SetImagingSettingsFunction{}, Stop: &imaging.StopFunction{}, -} +}*/ var MediaFunctionMap = map[string]Function{ AddAudioDecoderConfiguration: &media.AddAudioDecoderConfigurationFunction{}, diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index adc48dd..4c8106b 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -1840,8 +1840,8 @@ type RelayOutputSettings struct { } type DigitalInput struct { - Token ReferenceToken `xml:"token,attr"` - IdleState InputIdleState `xml:"IdleState,attr"` + Token ReferenceToken `xml:"token,attr"` + IdleState InputIdleState `xml:"IdleState,attr"` } // TODO:enumeration From add8ae2badecea3bf0564965bfaeaed168109e8a Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Mon, 25 Dec 2023 21:26:55 +0100 Subject: [PATCH 10/53] Revert "disable imaging" This reverts commit 6c5db5fed63f69109273dd175b53134f29102f6a. --- functionmap.go | 4 ++-- mappings.go | 5 +++-- xsd/onvif/onvif.go | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/functionmap.go b/functionmap.go index f6d0edb..49718a2 100644 --- a/functionmap.go +++ b/functionmap.go @@ -20,8 +20,8 @@ func FunctionByServiceAndFunctionName(serviceName, functionName string) (Functio functionMap = EventFunctionMap case AnalyticsWebService: functionMap = AnalyticsFunctionMap - //case ImagingWebService: - // functionMap = ImagingFunctionMap + case ImagingWebService: + functionMap = ImagingFunctionMap case RecordingWebService: functionMap = RecordingFunctionMap default: diff --git a/mappings.go b/mappings.go index 7bf4eb9..2f6726b 100644 --- a/mappings.go +++ b/mappings.go @@ -12,6 +12,7 @@ import ( "github.com/kerberos-io/onvif/analytics" "github.com/kerberos-io/onvif/device" "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/imaging" "github.com/kerberos-io/onvif/media" "github.com/kerberos-io/onvif/media2" "github.com/kerberos-io/onvif/ptz" @@ -140,7 +141,7 @@ var EventFunctionMap = map[string]Function{ Unsubscribe: &event.UnsubscribeFunction{}, } -/*var ImagingFunctionMap = map[string]Function{ +var ImagingFunctionMap = map[string]Function{ GetCurrentPreset: &imaging.GetCurrentPresetFunction{}, GetImagingSettings: &imaging.GetImagingSettingsFunction{}, GetMoveOptions: &imaging.GetMoveOptionsFunction{}, @@ -152,7 +153,7 @@ var EventFunctionMap = map[string]Function{ SetCurrentPreset: &imaging.SetCurrentPresetFunction{}, SetImagingSettings: &imaging.SetImagingSettingsFunction{}, Stop: &imaging.StopFunction{}, -}*/ +} var MediaFunctionMap = map[string]Function{ AddAudioDecoderConfiguration: &media.AddAudioDecoderConfigurationFunction{}, diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index 4c8106b..adc48dd 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -1840,8 +1840,8 @@ type RelayOutputSettings struct { } type DigitalInput struct { - Token ReferenceToken `xml:"token,attr"` - IdleState InputIdleState `xml:"IdleState,attr"` + Token ReferenceToken `xml:"token,attr"` + IdleState InputIdleState `xml:"IdleState,attr"` } // TODO:enumeration From 04f0dfbc03d89d1eac3b60f9dbf5c6c60bd9d030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Verstraeten?= Date: Mon, 25 Dec 2023 21:29:06 +0100 Subject: [PATCH 11/53] Delete Imaging/types.go --- Imaging/types.go | 108 ----------------------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 Imaging/types.go diff --git a/Imaging/types.go b/Imaging/types.go deleted file mode 100644 index adcd532..0000000 --- a/Imaging/types.go +++ /dev/null @@ -1,108 +0,0 @@ -package imaging - -//go:generate python3 ../python/gen_commands.py - -import ( - "github.com/kerberos-io/onvif/xsd" - "github.com/kerberos-io/onvif/xsd/onvif" -) - -type GetServiceCapabilities struct { - XMLName string `xml:"timg:GetServiceCapabilities"` -} - -// todo: fill in response type -type GetServiceCapabilitiesResponse struct { -} - -type GetImagingSettings struct { - XMLName string `xml:"timg:GetImagingSettings"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -type GetImagingSettingsResponse struct { - ImagingSettings onvif.ImagingSettings20 `xml:"timg:ImagingSettings"` -} - -type SetImagingSettings struct { - XMLName string `xml:"timg:SetImagingSettings"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` - ImagingSettings onvif.ImagingSettings20 `xml:"timg:ImagingSettings"` - ForcePersistence xsd.Boolean `xml:"timg:ForcePersistence"` -} - -type SetImagingSettingsResponse struct { -} - -type GetOptions struct { - XMLName string `xml:"timg:GetOptions"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -// todo: fill in response type -type GetOptionsResponse struct { -} - -type Move struct { - XMLName string `xml:"timg:Move"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` - Focus onvif.FocusMove `xml:"timg:Focus"` -} - -// todo: fill in response type -type MoveResponse struct { -} - -type GetMoveOptions struct { - XMLName string `xml:"timg:GetMoveOptions"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -// todo: fill in response type -type GetMoveOptionsResponse struct { -} - -type Stop struct { - XMLName string `xml:"timg:Stop"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -// todo: fill in response type -type StopResponse struct { -} - -type GetStatus struct { - XMLName string `xml:"timg:GetStatus"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -// todo: fill in response type -type GetStatusResponse struct { -} - -type GetPresets struct { - XMLName string `xml:"timg:GetPresets"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -// todo: fill in response type -type GetPresetsResponse struct { -} - -type GetCurrentPreset struct { - XMLName string `xml:"timg:GetCurrentPreset"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` -} - -// todo: fill in response type -type GetCurrentPresetResponse struct { -} - -type SetCurrentPreset struct { - XMLName string `xml:"timg:SetCurrentPreset"` - VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` - PresetToken onvif.ReferenceToken `xml:"timg:PresetToken"` -} - -type SetCurrentPresetResponse struct { -} From 4c9f12fc975d84982c70983437fee197b8ec9bb5 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Mon, 25 Dec 2023 21:31:14 +0100 Subject: [PATCH 12/53] add --- imaging/types.go | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 imaging/types.go diff --git a/imaging/types.go b/imaging/types.go new file mode 100644 index 0000000..adcd532 --- /dev/null +++ b/imaging/types.go @@ -0,0 +1,108 @@ +package imaging + +//go:generate python3 ../python/gen_commands.py + +import ( + "github.com/kerberos-io/onvif/xsd" + "github.com/kerberos-io/onvif/xsd/onvif" +) + +type GetServiceCapabilities struct { + XMLName string `xml:"timg:GetServiceCapabilities"` +} + +// todo: fill in response type +type GetServiceCapabilitiesResponse struct { +} + +type GetImagingSettings struct { + XMLName string `xml:"timg:GetImagingSettings"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +type GetImagingSettingsResponse struct { + ImagingSettings onvif.ImagingSettings20 `xml:"timg:ImagingSettings"` +} + +type SetImagingSettings struct { + XMLName string `xml:"timg:SetImagingSettings"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` + ImagingSettings onvif.ImagingSettings20 `xml:"timg:ImagingSettings"` + ForcePersistence xsd.Boolean `xml:"timg:ForcePersistence"` +} + +type SetImagingSettingsResponse struct { +} + +type GetOptions struct { + XMLName string `xml:"timg:GetOptions"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +// todo: fill in response type +type GetOptionsResponse struct { +} + +type Move struct { + XMLName string `xml:"timg:Move"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` + Focus onvif.FocusMove `xml:"timg:Focus"` +} + +// todo: fill in response type +type MoveResponse struct { +} + +type GetMoveOptions struct { + XMLName string `xml:"timg:GetMoveOptions"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +// todo: fill in response type +type GetMoveOptionsResponse struct { +} + +type Stop struct { + XMLName string `xml:"timg:Stop"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +// todo: fill in response type +type StopResponse struct { +} + +type GetStatus struct { + XMLName string `xml:"timg:GetStatus"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +// todo: fill in response type +type GetStatusResponse struct { +} + +type GetPresets struct { + XMLName string `xml:"timg:GetPresets"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +// todo: fill in response type +type GetPresetsResponse struct { +} + +type GetCurrentPreset struct { + XMLName string `xml:"timg:GetCurrentPreset"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` +} + +// todo: fill in response type +type GetCurrentPresetResponse struct { +} + +type SetCurrentPreset struct { + XMLName string `xml:"timg:SetCurrentPreset"` + VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` + PresetToken onvif.ReferenceToken `xml:"timg:PresetToken"` +} + +type SetCurrentPresetResponse struct { +} From d1b78fa51abad3b28f55ee37eb723fb8e0989879 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Tue, 20 Aug 2024 09:00:32 +0200 Subject: [PATCH 13/53] make array of relayoutputs and digitalinputs --- device/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/device/types.go b/device/types.go index 521dc7c..708b67e 100644 --- a/device/types.go +++ b/device/types.go @@ -680,7 +680,7 @@ type GetRelayOutputs struct { } type GetRelayOutputsResponse struct { - RelayOutputs onvif.RelayOutput + RelayOutputs []onvif.RelayOutput } type GetDigitalInputs struct { @@ -688,7 +688,7 @@ type GetDigitalInputs struct { } type GetDigitalInputsResponse struct { - DigitalInputs onvif.DigitalInput + DigitalInputs []onvif.DigitalInput } type SetRelayOutputSettings struct { From ee8a91993243c628338a5d204a70da7874e707c7 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Wed, 21 Aug 2024 16:07:06 +0200 Subject: [PATCH 14/53] support for latest hikivision ONVIF 19.12 --- Device.go | 54 ++++++++++++++++++++++++++--- api/api.go | 4 ++- deviceio/types.go | 4 +-- go.mod | 2 ++ go.sum | 12 +++++-- names.go | 2 +- networking/networking.go | 75 +++++++++++++++++++++++++++++++++++++++- xsd/onvif/onvif.go | 4 +-- 8 files changed, 143 insertions(+), 14 deletions(-) diff --git a/Device.go b/Device.go index ec4c258..fec909e 100644 --- a/Device.go +++ b/Device.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" + "github.com/kerberos-io/onvif/networking" "github.com/kerberos-io/onvif/xsd/onvif" "github.com/beevik/etree" @@ -253,7 +254,7 @@ func (dev *Device) getEndpoint(endpoint string) (string, error) { // CallMethod functions call an method, defined struct. // You should use Authenticate method to call authorized requests. -func (dev *Device) CallMethod(method interface{}) (*http.Response, error) { +func (dev Device) CallMethod(method interface{}) (*http.Response, error) { pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/") pkg := strings.ToLower(pkgPath[len(pkgPath)-1]) @@ -261,11 +262,35 @@ func (dev *Device) CallMethod(method interface{}) (*http.Response, error) { if err != nil { return nil, err } - requestBody, err := xml.Marshal(method) + return dev.callMethodDo(endpoint, method) +} + +// CallMethod functions call an method, defined struct with authentication data +func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) { + output, err := xml.MarshalIndent(method, " ", " ") if err != nil { return nil, err } - return dev.SendSoap(endpoint, string(requestBody)) + + soap, err := dev.buildMethodSOAP(string(output)) + if err != nil { + return nil, err + } + + soap.AddRootNamespaces(Xlmns) + soap.AddAction() + + //Auth Handling + if dev.params.Username != "" && dev.params.Password != "" { + soap.AddWSSecurity(dev.params.Username, dev.params.Password) + } + + servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) + if err != nil { + servResp, err = networking.SendSoapWithDigest(new(http.Client), endpoint, soap.String(), dev.params.Username, dev.params.Password) + } + + return servResp, err } func (dev *Device) GetDeviceParams() DeviceParams { @@ -283,7 +308,7 @@ func (dev *Device) GetEndpointByRequestStruct(requestStruct interface{}) (string return endpoint, err } -func (dev *Device) SendSoap(endpoint string, xmlRequestBody string) (resp *http.Response, err error) { +/*func (dev *Device) SendSoap(endpoint string, xmlRequestBody string) (resp *http.Response, err error) { soap := gosoap.NewEmptySOAP() soap.AddStringBodyContent(xmlRequestBody) soap.AddRootNamespaces(Xlmns) @@ -302,6 +327,27 @@ func (dev *Device) SendSoap(endpoint string, xmlRequestBody string) (resp *http. resp, err = dev.params.HttpClient.Do(req) } return resp, err +}*/ + +// CallMethod functions call an method, defined struct with authentication data +func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Response, error) { + + soap := gosoap.NewEmptySOAP() + soap.AddStringBodyContent(xmlRequestBody) + soap.AddRootNamespaces(Xlmns) + soap.AddAction() + + //Auth Handling + if dev.params.Username != "" && dev.params.Password != "" { + soap.AddWSSecurity(dev.params.Username, dev.params.Password) + } + + servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) + if err != nil { + servResp, err = networking.SendSoapWithDigest(new(http.Client), endpoint, soap.String(), dev.params.Username, dev.params.Password) + } + + return servResp, err } func createHttpRequest(httpMethod string, endpoint string, soap string) (req *http.Request, err error) { diff --git a/api/api.go b/api/api.go index ca0ac12..bbe50d9 100644 --- a/api/api.go +++ b/api/api.go @@ -122,6 +122,8 @@ func callNecessaryMethod(serviceName, methodName, acceptedData, username, passwo switch strings.ToLower(serviceName) { case "device": methodStruct, err = getDeviceStructByName(methodName) + case "deviceio": + methodStruct, err = getDeviceStructByName(methodName) case "ptz": methodStruct, err = getPTZStructByName(methodName) case "media": @@ -150,7 +152,7 @@ func callNecessaryMethod(serviceName, methodName, acceptedData, username, passwo servResp, err := networking.SendSoap(new(http.Client), endpoint, soap.String()) if err != nil { - return "", err + servResp, err = networking.SendSoapWithDigest(new(http.Client), endpoint, soap.String(), username, password) } rsp, err := ioutil.ReadAll(servResp.Body) diff --git a/deviceio/types.go b/deviceio/types.go index 4369589..9bff659 100644 --- a/deviceio/types.go +++ b/deviceio/types.go @@ -680,7 +680,7 @@ type GetRelayOutputs struct { } type GetRelayOutputsResponse struct { - RelayOutputs onvif.RelayOutput + RelayOutputs []onvif.RelayOutput } type GetDigitalInputs struct { @@ -688,7 +688,7 @@ type GetDigitalInputs struct { } type GetDigitalInputsResponse struct { - DigitalInputs onvif.DigitalInput + DigitalInputs []onvif.DigitalInput } type SetRelayOutputSettings struct { diff --git a/go.mod b/go.mod index f981751..f98d94f 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae github.com/gin-gonic/gin v1.9.1 github.com/google/uuid v1.4.0 + github.com/icholy/digest v0.1.23 + github.com/juju/errors v1.0.0 github.com/stretchr/testify v1.8.4 golang.org/x/net v0.19.0 ) diff --git a/go.sum b/go.sum index 5207087..b3fac64 100644 --- a/go.sum +++ b/go.sum @@ -29,16 +29,22 @@ github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QX github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/icholy/digest v0.1.23 h1:4hX2pIloP0aDx7RJW0JewhPPy3R8kU+vWKdxPsCCGtY= +github.com/icholy/digest v0.1.23/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= +github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= @@ -81,14 +87,14 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/names.go b/names.go index 4a31c8e..5b9cb22 100644 --- a/names.go +++ b/names.go @@ -75,7 +75,7 @@ const ( GetNetworkProtocols = "GetNetworkProtocols" GetPkcs10Request = "GetPkcs10Request" GetRelayOutputs = "GetRelayOutputs" - GetDigitalInputs = "GetDigitalInputs" + GetDigitalInputs = "GetDigitalInputs" GetRemoteDiscoveryMode = "GetRemoteDiscoveryMode" GetRemoteUser = "GetRemoteUser" GetScopes = "GetScopes" diff --git a/networking/networking.go b/networking/networking.go index 7e1aef2..9903be4 100644 --- a/networking/networking.go +++ b/networking/networking.go @@ -2,14 +2,87 @@ package networking import ( "bytes" + "fmt" + "io" "net/http" + + "github.com/beevik/etree" + "github.com/icholy/digest" + "github.com/juju/errors" ) // SendSoap send soap message func SendSoap(httpClient *http.Client, endpoint, message string) (*http.Response, error) { resp, err := httpClient.Post(endpoint, "application/soap+xml; charset=utf-8", bytes.NewBufferString(message)) if err != nil { - return resp, err + return resp, errors.Annotate(err, "Post") + } + + // if resp.StatusCode is 4xx,5xx, return error + if resp.StatusCode >= 400 && resp.StatusCode < 600 { + return resp, errors.Errorf("Server error: %d: %s", resp.StatusCode, resp.Status) + } + + return resp, nil +} + +func SendSoapWithDigest(httpClient *http.Client, endpoint, message, username, password string) (*http.Response, error) { + doc := etree.NewDocument() + if err := doc.ReadFromString(message); err != nil { + return nil, err + } + + e := doc.FindElement("./Envelope/Header/Security") + if e != nil { + bodyTag := doc.Root().SelectElement("Header") + bodyTag.RemoveChild(e) + data, err := doc.WriteToString() + if err != nil { + return nil, err + } + message = data + } + + req, err := http.NewRequest("POST", endpoint, bytes.NewBufferString(message)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/soap+xml; charset=utf-8") + resp, err := httpClient.Do(req) + if err != nil { + fmt.Println(err) + return resp, errors.Annotate(err, "Post with digest") + } + + if resp.StatusCode != http.StatusUnauthorized { + return resp, err + } + + wwwAuth := resp.Header.Get("WWW-Authenticate") + chal, err := digest.ParseChallenge(wwwAuth) + if err != nil { + return resp, fmt.Errorf("fail to parse challenge: %w", err) + } + + cred, err := digest.Digest(chal, digest.Options{ + Method: "POST", + URI: req.URL.RequestURI(), + Username: username, + Password: password, + }) + + if err != nil { + return resp, fmt.Errorf("fail to build digest: %w", err) + } + + req.Header.Add("Authorization", cred.String()) + req.Body = io.NopCloser((bytes.NewBufferString(message))) + resp, err = httpClient.Do(req) + if err != nil { + return nil, errors.Annotate(err, "Post with digest") + } + if resp.StatusCode >= 400 && resp.StatusCode < 600 { + return resp, errors.Errorf("Post with digest error: %d: %s", resp.StatusCode, resp.Status) } return resp, nil diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index adc48dd..4c8106b 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -1840,8 +1840,8 @@ type RelayOutputSettings struct { } type DigitalInput struct { - Token ReferenceToken `xml:"token,attr"` - IdleState InputIdleState `xml:"IdleState,attr"` + Token ReferenceToken `xml:"token,attr"` + IdleState InputIdleState `xml:"IdleState,attr"` } // TODO:enumeration From 6fc6d9a99eb102f4cd669d15af32886377168e8d Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Thu, 16 Jan 2025 21:43:28 +0100 Subject: [PATCH 15/53] reuse main http client + resolved memory leak (close previous response) before creating new one. --- Device.go | 6 ++++-- networking/networking.go | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Device.go b/Device.go index fec909e..d1ec5df 100644 --- a/Device.go +++ b/Device.go @@ -287,7 +287,7 @@ func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Respo servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) if err != nil { - servResp, err = networking.SendSoapWithDigest(new(http.Client), endpoint, soap.String(), dev.params.Username, dev.params.Password) + servResp, err = networking.SendSoapWithDigest(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password) } return servResp, err @@ -344,7 +344,9 @@ func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Respon servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) if err != nil { - servResp, err = networking.SendSoapWithDigest(new(http.Client), endpoint, soap.String(), dev.params.Username, dev.params.Password) + // Close server response body to reuse the connection + servResp.Body.Close() + servResp, err = networking.SendSoapWithDigest(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password) } return servResp, err diff --git a/networking/networking.go b/networking/networking.go index 9903be4..c90c231 100644 --- a/networking/networking.go +++ b/networking/networking.go @@ -50,7 +50,6 @@ func SendSoapWithDigest(httpClient *http.Client, endpoint, message, username, pa req.Header.Set("Content-Type", "application/soap+xml; charset=utf-8") resp, err := httpClient.Do(req) if err != nil { - fmt.Println(err) return resp, errors.Annotate(err, "Post with digest") } @@ -75,6 +74,9 @@ func SendSoapWithDigest(httpClient *http.Client, endpoint, message, username, pa return resp, fmt.Errorf("fail to build digest: %w", err) } + // Readout body to close the connection + resp.Body.Close() + req.Header.Add("Authorization", cred.String()) req.Body = io.NopCloser((bytes.NewBufferString(message))) resp, err = httpClient.Do(req) From 542f83433b77b36ddbb58e626235c6c08b2559b9 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Sat, 18 Jan 2025 09:09:33 +0100 Subject: [PATCH 16/53] Update Device.go --- Device.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Device.go b/Device.go index d1ec5df..6fa86b6 100644 --- a/Device.go +++ b/Device.go @@ -287,6 +287,8 @@ func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Respo servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) if err != nil { + // Close server response body to reuse the connection + servResp.Body.Close() servResp, err = networking.SendSoapWithDigest(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password) } From f3f03cc827fd46ea3261ca6e7f6e63efb15fc67e Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Sat, 18 Jan 2025 09:12:05 +0100 Subject: [PATCH 17/53] add pr description workflow --- .github/pr-description.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/pr-description.yml diff --git a/.github/pr-description.yml b/.github/pr-description.yml new file mode 100644 index 0000000..f607658 --- /dev/null +++ b/.github/pr-description.yml @@ -0,0 +1,19 @@ +name: Autofill PR description + +on: pull_request + +jobs: + openai-pr-description: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + - name: Autofill PR description if empty using OpenAI + uses: cedricve/azureopenai-pr-description@master + with: + github_token: ${{ secrets.TOKEN }} + openai_api_key: ${{ secrets.OPENAI_API_KEY }} + azure_openai_api_key: ${{ secrets.AZURE_OPENAI_API_KEY }} + azure_openai_endpoint: ${{ secrets.AZURE_OPENAI_ENDPOINT }} + azure_openai_version: ${{ secrets.AZURE_OPENAI_VERSION }} + overwrite_description: true \ No newline at end of file From ede8b81fc82588118930f2b368be12444e956294 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Sun, 19 Jan 2025 09:58:53 +0100 Subject: [PATCH 18/53] Update Device.go --- Device.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Device.go b/Device.go index 6fa86b6..c7b0cce 100644 --- a/Device.go +++ b/Device.go @@ -288,7 +288,9 @@ func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Respo servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) if err != nil { // Close server response body to reuse the connection - servResp.Body.Close() + if servResp != nil { + servResp.Body.Close() + } servResp, err = networking.SendSoapWithDigest(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password) } @@ -347,7 +349,9 @@ func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Respon servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) if err != nil { // Close server response body to reuse the connection - servResp.Body.Close() + if servResp != nil { + servResp.Body.Close() + } servResp, err = networking.SendSoapWithDigest(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password) } From 9ca534b5bba64bc7df7bea2b12158e1042bafce9 Mon Sep 17 00:00:00 2001 From: Cedric Verstraeten Date: Sun, 19 Jan 2025 10:19:22 +0100 Subject: [PATCH 19/53] correct workflow structure --- .github/{ => workflows}/pr-description.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/pr-description.yml (100%) diff --git a/.github/pr-description.yml b/.github/workflows/pr-description.yml similarity index 100% rename from .github/pr-description.yml rename to .github/workflows/pr-description.yml From 9f922382752a18fc23b3542b4ebc86ba53270ae0 Mon Sep 17 00:00:00 2001 From: stefan van der lee Date: Mon, 28 Apr 2025 18:02:14 +0200 Subject: [PATCH 20/53] Add UtcTime attribute to MessageDescription struct --- event/types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/event/types.go b/event/types.go index a980e12..22056ce 100644 --- a/event/types.go +++ b/event/types.go @@ -133,6 +133,7 @@ type MessageBody struct { type MessageDescription struct { PropertyOperation xsd.AnyType `xml:"PropertyOperation,attr"` + UtcTime xsd.AnyType `xml:"UtcTime,attr"` Source Source `json:",omitempty" xml:",omitempty"` Data Data `json:",omitempty" xml:",omitempty"` } From 4fc5593195fcabddee9b398f012667a4f88c0ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Verstraeten?= Date: Tue, 20 May 2025 12:13:39 +0200 Subject: [PATCH 21/53] Add Dockerfile and devcontainer.json for development environment setup --- .devcontainer/Dockerfile | 8 ++++++++ .devcontainer/devcontainer.json | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..ada3537 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,8 @@ +FROM mcr.microsoft.com/devcontainers/go:1.24-bookworm + +# Install node environment +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + nodejs \ + npm \ + && rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..900432a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,15 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "go:1.24.2-bookworm", + "dockerFile": "Dockerfile", + "customizations": { + "vscode": { + "extensions": [ + "GitHub.copilot", + "golang.go", + "GitHub.vscode-pull-request-github" + ] + } + } +} \ No newline at end of file From 03101ec2716c551daa7d26387a80d73af74fdb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Verstraeten?= Date: Tue, 20 May 2025 10:14:19 +0000 Subject: [PATCH 22/53] Remove custom color settings from VSCode configuration --- .vscode/settings.json | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d58c7a7..4e7866b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,24 +1,6 @@ { "editor.tabSize": 2, "extensions.ignoreRecommendations": true, - "workbench.colorCustomizations": { - "activityBar.background": "#759570", - "activityBar.activeBorder": "#cfd3ef", - "activityBar.foreground": "#e7e7e7", - "activityBar.hoverBackground": "#352cea", - "activityBar.inactiveForeground": "#e7e7e799", - "activityBarBadge.background": "#cfd3ef", - "activityBarBadge.foreground": "#15202b", - "titleBar.activeBackground": "#5e7959", - "titleBar.inactiveBackground": "#5e795999", - "titleBar.activeForeground": "#e7e7e7", - "titleBar.inactiveForeground": "#e7e7e799", - "statusBarItem.hoverBackground": "#352cea", - "statusBar.foreground": "#e7e7e7", - "panel.border": "#759570", - "sideBar.border": "#759570", - "editorGroup.border": "#759570" - }, "go.languageServerFlags": [], "go.lintOnSave": "file", "go.vetOnSave": "package", From 174de6954b1f38a1ee6bc226e8756ca59d3137fb Mon Sep 17 00:00:00 2001 From: Mohit Solanki Date: Sat, 21 Feb 2026 18:02:09 +0530 Subject: [PATCH 23/53] fix: support multiple TourSpots in PresetTour struct. TourSpot was defined as a single PTZPresetTourSpot value, making it impossible to configure a preset tour with more than one stop. The ONVIF spec allows multiple TourSpot entries per tour, so this changes the field to a slice. --- ptz/types.go | 2 +- xsd/onvif/onvif.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ptz/types.go b/ptz/types.go index e07c3fc..101e59a 100644 --- a/ptz/types.go +++ b/ptz/types.go @@ -220,7 +220,7 @@ type GetPresetTours struct { } type GetPresetToursResponse struct { - PresetTour onvif.PresetTour + PresetTour []onvif.PresetTour } type GetPresetTour struct { diff --git a/xsd/onvif/onvif.go b/xsd/onvif/onvif.go index 4c8106b..9cc246a 100644 --- a/xsd/onvif/onvif.go +++ b/xsd/onvif/onvif.go @@ -1176,7 +1176,7 @@ type PresetTour struct { Status PTZPresetTourStatus `xml:"Status"` AutoStart xsd.Boolean `xml:"AutoStart"` StartingCondition PTZPresetTourStartingCondition `xml:"StartingCondition"` - TourSpot PTZPresetTourSpot `xml:"TourSpot"` + TourSpot []PTZPresetTourSpot `xml:"TourSpot"` Extension PTZPresetTourExtension `xml:"Extension"` } From ce67879ee52c693a9fd2041b5dfce7a8fbe13703 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 13:57:21 +0200 Subject: [PATCH 24/53] feat(event/stream): scaffold package with normalized event types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a new event/stream sub-package that will host the long-running, channel-based consumer for ONVIF device events. This commit only lays down the value types — EventKind, EventState, PropertyOperation and the Event struct — together with Stringer methods and zero-value tests. The intent is to give callers a vendor-neutral surface (Motion, DigitalInput, etc.) so they do not need to special-case AXIS, Hikvision, Avigilon, Hanwha, Bosch or Dahua topic strings. Decoding, topic classification and the Stream type itself land in follow-up commits. --- event/stream/doc.go | 13 ++++ event/stream/types.go | 130 +++++++++++++++++++++++++++++++++++++ event/stream/types_test.go | 83 +++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 event/stream/doc.go create mode 100644 event/stream/types.go create mode 100644 event/stream/types_test.go diff --git a/event/stream/doc.go b/event/stream/doc.go new file mode 100644 index 0000000..ba0f79b --- /dev/null +++ b/event/stream/doc.go @@ -0,0 +1,13 @@ +// Package stream provides a long-running, channel-based consumer for ONVIF +// device events. It hides the SOAP/XML, pull-point lifecycle, renewal and +// vendor-specific topic conventions behind a typed Event stream. +// +// A Stream is created with NewStream and yields decoded Event values on the +// channel returned by Events. Non-fatal errors (transient SOAP failures that +// the stream recovers from) are surfaced on Errors. The Stream is stopped by +// cancelling the context passed to NewStream or by calling Close. +// +// The package classifies vendor-specific topic strings (AXIS, Hikvision, +// Avigilon, Hanwha, Bosch, Dahua) into a small set of normalized EventKind +// values so callers do not need to special-case device manufacturers. +package stream diff --git a/event/stream/types.go b/event/stream/types.go new file mode 100644 index 0000000..3d0bae1 --- /dev/null +++ b/event/stream/types.go @@ -0,0 +1,130 @@ +package stream + +import ( + "fmt" + "time" +) + +// EventKind is the normalized category of an ONVIF event, independent of the +// camera vendor's topic naming. +type EventKind uint8 + +const ( + // KindUnknown is the zero value; used when a topic does not match any + // known classification. + KindUnknown EventKind = iota + // KindMotion covers motion detection from any vendor (e.g. AXIS + // VideoSource/MotionAlarm, Hikvision RuleEngine/CellMotionDetector). + KindMotion + // KindTampering covers camera tampering / scene change alarms. + KindTampering + // KindDigitalInput covers external sensor inputs wired to the camera. + KindDigitalInput + // KindDigitalOutput covers relay output state changes on the camera. + KindDigitalOutput + // KindObjectDetected covers analytics-based object/person/vehicle + // detection events. + KindObjectDetected + // KindAudioAlarm covers audio-level / loud-noise alarms. + KindAudioAlarm +) + +// String implements fmt.Stringer. +func (k EventKind) String() string { + switch k { + case KindUnknown: + return "Unknown" + case KindMotion: + return "Motion" + case KindTampering: + return "Tampering" + case KindDigitalInput: + return "DigitalInput" + case KindDigitalOutput: + return "DigitalOutput" + case KindObjectDetected: + return "ObjectDetected" + case KindAudioAlarm: + return "AudioAlarm" + default: + return fmt.Sprintf("EventKind(%d)", uint8(k)) + } +} + +// EventState is the active/inactive state carried by an event. Most ONVIF +// alarms are boolean (e.g. IsMotion=true/false); StateUnknown is used when +// the value cannot be parsed. +type EventState uint8 + +const ( + StateUnknown EventState = iota + StateActive + StateInactive +) + +// String implements fmt.Stringer. +func (s EventState) String() string { + switch s { + case StateUnknown: + return "Unknown" + case StateActive: + return "Active" + case StateInactive: + return "Inactive" + default: + return fmt.Sprintf("EventState(%d)", uint8(s)) + } +} + +// PropertyOperation mirrors the ONVIF wsnt:PropertyOperation attribute and +// indicates whether a message is the first sighting of a property +// (Initialized), a transition (Changed) or the property going away (Deleted). +type PropertyOperation uint8 + +const ( + PropertyUnknown PropertyOperation = iota + PropertyInitialized + PropertyChanged + PropertyDeleted +) + +// String implements fmt.Stringer. +func (p PropertyOperation) String() string { + switch p { + case PropertyUnknown: + return "Unknown" + case PropertyInitialized: + return "Initialized" + case PropertyChanged: + return "Changed" + case PropertyDeleted: + return "Deleted" + default: + return fmt.Sprintf("PropertyOperation(%d)", uint8(p)) + } +} + +// Event is a single normalized notification from an ONVIF device. +// +// Kind, State and Operation are the normalized fields most callers should +// switch on. Topic, RawValue and Source preserve the original ONVIF data so +// callers can do further inspection or logging without re-parsing SOAP. +type Event struct { + // Kind is the normalized event category. + Kind EventKind + // State is the active/inactive value carried by the event. + State EventState + // Operation is the ONVIF property lifecycle (Initialized/Changed/Deleted). + Operation PropertyOperation + // Source identifies the channel, input, or rule that produced the event + // (taken from the Source SimpleItem in the notification). + Source string + // Topic is the raw ONVIF topic string, e.g. tns1:VideoSource/MotionAlarm. + Topic string + // RawValue is the unparsed Data SimpleItem value (e.g. "true", "1", + // "active") so callers can read non-boolean values when needed. + RawValue string + // Timestamp is when the stream observed the event locally. The ONVIF + // UtcTime is not used because clocks on many cameras drift. + Timestamp time.Time +} diff --git a/event/stream/types_test.go b/event/stream/types_test.go new file mode 100644 index 0000000..45d4069 --- /dev/null +++ b/event/stream/types_test.go @@ -0,0 +1,83 @@ +package stream + +import ( + "testing" + "time" +) + +func TestEventKindString(t *testing.T) { + tests := []struct { + kind EventKind + want string + }{ + {KindUnknown, "Unknown"}, + {KindMotion, "Motion"}, + {KindTampering, "Tampering"}, + {KindDigitalInput, "DigitalInput"}, + {KindDigitalOutput, "DigitalOutput"}, + {KindObjectDetected, "ObjectDetected"}, + {KindAudioAlarm, "AudioAlarm"}, + {EventKind(255), "EventKind(255)"}, + } + for _, tc := range tests { + if got := tc.kind.String(); got != tc.want { + t.Errorf("EventKind(%d).String() = %q, want %q", tc.kind, got, tc.want) + } + } +} + +func TestEventStateString(t *testing.T) { + tests := []struct { + state EventState + want string + }{ + {StateUnknown, "Unknown"}, + {StateActive, "Active"}, + {StateInactive, "Inactive"}, + {EventState(255), "EventState(255)"}, + } + for _, tc := range tests { + if got := tc.state.String(); got != tc.want { + t.Errorf("EventState(%d).String() = %q, want %q", tc.state, got, tc.want) + } + } +} + +func TestPropertyOperationString(t *testing.T) { + tests := []struct { + op PropertyOperation + want string + }{ + {PropertyUnknown, "Unknown"}, + {PropertyInitialized, "Initialized"}, + {PropertyChanged, "Changed"}, + {PropertyDeleted, "Deleted"}, + {PropertyOperation(255), "PropertyOperation(255)"}, + } + for _, tc := range tests { + if got := tc.op.String(); got != tc.want { + t.Errorf("PropertyOperation(%d).String() = %q, want %q", tc.op, got, tc.want) + } + } +} + +func TestEventZeroValue(t *testing.T) { + var e Event + if e.Kind != KindUnknown { + t.Errorf("zero Event.Kind = %v, want KindUnknown", e.Kind) + } + if e.State != StateUnknown { + t.Errorf("zero Event.State = %v, want StateUnknown", e.State) + } + if !e.Timestamp.IsZero() { + t.Errorf("zero Event.Timestamp = %v, want zero time", e.Timestamp) + } +} + +func TestEventTimestampPreserved(t *testing.T) { + now := time.Now() + e := Event{Timestamp: now} + if !e.Timestamp.Equal(now) { + t.Errorf("Event.Timestamp = %v, want %v", e.Timestamp, now) + } +} From 2cc266714e4befa481c7c380acfab133abe5bce9 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 13:59:22 +0200 Subject: [PATCH 25/53] feat(event/stream): classify vendor topics to normalized EventKind Adds a Classify function that maps ONVIF topic strings to EventKind so the agent does not need to know AXIS vs Hikvision vs Bosch topic conventions. The classifier canonicalizes topics by stripping XML-namespace prefixes from each path segment, which collapses vendor variants like 'tns1:Device/tnssamsung:DigitalInput' and 'tns1:Device/Trigger/DigitalInput' to a single matchable form. Motion coverage on day one: * tns1:VideoSource/MotionAlarm (AXIS, Bosch, Dahua, ...) * tns1:VideoAnalytics/:MotionAlarm * tns1:RuleEngine/CellMotionDetector/Motion (ONVIF standard, Hikvision) * tns1:RuleEngine/MotionRegionDetector/Motion (AXIS region rule) * tnsaxis:CameraApplicationPlatform/ObjectAnalytics/... Also covers Tamper, DigitalInput, Relay (DigitalOutput), object analytics and audio alarms, so the same Stream can replace the agent's ad-hoc digital I/O polling without losing coverage. --- event/stream/topics.go | 72 +++++++++++++++++++++++++++++++++++++ event/stream/topics_test.go | 53 +++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 event/stream/topics.go create mode 100644 event/stream/topics_test.go diff --git a/event/stream/topics.go b/event/stream/topics.go new file mode 100644 index 0000000..35b1bf4 --- /dev/null +++ b/event/stream/topics.go @@ -0,0 +1,72 @@ +package stream + +import "strings" + +// Classify maps an ONVIF topic string (e.g. "tns1:VideoSource/MotionAlarm") +// to the normalized EventKind that callers should switch on. Returns +// KindUnknown when no rule matches. +// +// The classifier strips XML-namespace prefixes from each path segment so it +// is robust to vendor-specific namespaces like tnsaxis:, tnsbosch:, +// tnssamsung:. Matching is case-sensitive because ONVIF topic identifiers +// are case-sensitive per the spec. +func Classify(topic string) EventKind { + if topic == "" { + return KindUnknown + } + canonical := canonicalizeTopic(topic) + for _, rule := range topicRules { + if strings.Contains(canonical, rule.needle) { + return rule.kind + } + } + return KindUnknown +} + +// canonicalizeTopic strips the XML-namespace prefix (e.g. "tns1:") from each +// "/"-separated segment of the topic. This collapses vendor variants like +// "tns1:Device/tnssamsung:DigitalInput" and the plain +// "tns1:Device/DigitalInput" form to the same canonical path. +func canonicalizeTopic(topic string) string { + segments := strings.Split(topic, "/") + for i, seg := range segments { + if idx := strings.Index(seg, ":"); idx >= 0 { + segments[i] = seg[idx+1:] + } + } + return strings.Join(segments, "/") +} + +// topicRules is evaluated in order; first match wins. Keep the most specific +// rules first when adding new entries — e.g. "MotionDetector/Motion" must +// precede a hypothetical bare "/Motion" rule. +var topicRules = []struct { + needle string + kind EventKind +}{ + // Motion: covers AXIS VideoSource/MotionAlarm, ONVIF + // RuleEngine/CellMotionDetector and MotionRegionDetector, and + // vendor-namespaced MotionAlarm variants (Bosch). + {"MotionAlarm", KindMotion}, + {"CellMotionDetector/Motion", KindMotion}, + {"MotionRegionDetector/Motion", KindMotion}, + + // Tampering: ONVIF RuleEngine/TamperDetector. + {"TamperDetector", KindTampering}, + + // Digital I/O: ONVIF Device/Trigger/{DigitalInput,Relay}. The + // canonicalization step normalizes vendor-prefixed inner segments + // (tnssamsung:DigitalInput, tns1:Relay) to the bare names. + {"Trigger/DigitalInput", KindDigitalInput}, + {"Trigger/Relay", KindDigitalOutput}, + + // Object analytics: AXIS ObjectAnalytics scenarios use dynamic suffixes + // (Device1ScenarioANY, Device1Scenario1, ...), so match the path prefix. + {"ObjectAnalytics/", KindObjectDetected}, + {"ObjectsInside", KindObjectDetected}, + + // Audio: ONVIF AudioAnalytics/Audio/DetectedSound and AXIS + // AudioSource/TriggerLevel. + {"Audio/DetectedSound", KindAudioAlarm}, + {"AudioSource/TriggerLevel", KindAudioAlarm}, +} diff --git a/event/stream/topics_test.go b/event/stream/topics_test.go new file mode 100644 index 0000000..1477d47 --- /dev/null +++ b/event/stream/topics_test.go @@ -0,0 +1,53 @@ +package stream + +import "testing" + +func TestClassifyTopic(t *testing.T) { + tests := []struct { + name string + topic string + want EventKind + }{ + // AXIS: motion alarm on video source. + {"axis_video_source_motion", "tns1:VideoSource/MotionAlarm", KindMotion}, + // AXIS / Hikvision / others: ONVIF cell-motion detector rule. + {"cell_motion_detector", "tns1:RuleEngine/CellMotionDetector/Motion", KindMotion}, + // AXIS region motion. + {"motion_region_detector", "tns1:RuleEngine/MotionRegionDetector/Motion", KindMotion}, + // Vendor-prefixed motion (e.g. Bosch). + {"bosch_motion", "tns1:VideoAnalytics/tnsbosch:MotionAlarm", KindMotion}, + // Tampering / scene change. + {"tamper_detector", "tns1:RuleEngine/TamperDetector/Tamper", KindTampering}, + {"axis_scene_tamper", "tns1:VideoSource/ImageTooDark/ImagingService", KindUnknown}, // not classified + // Digital input — vendor-namespaced variant from agent code. + {"digital_input", "tns1:Device/Trigger/DigitalInput", KindDigitalInput}, + {"digital_input_samsung", "tns1:Device/tns1:Trigger/tnssamsung:DigitalInput", KindDigitalInput}, + // Digital output / relay. + {"relay", "tns1:Device/Trigger/Relay", KindDigitalOutput}, + {"relay_avigilon", "tns1:Device/tns1:Trigger/tns1:Relay", KindDigitalOutput}, + // Analytics object detection (AXIS object analytics, generic motion analytics). + {"object_detected", "tns1:RuleEngine/MyRuleDetector/ObjectsInside", KindObjectDetected}, + {"axis_object_analytics", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1ScenarioANY", KindObjectDetected}, + // Audio. + {"audio_alarm", "tns1:AudioAnalytics/Audio/DetectedSound", KindAudioAlarm}, + {"axis_audio", "tnsaxis:AudioSource/TriggerLevel", KindAudioAlarm}, + // Unknown — should not be force-classified. + {"empty", "", KindUnknown}, + {"unknown_topic", "tns1:UserAlarm/IVA", KindUnknown}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := Classify(tc.topic); got != tc.want { + t.Errorf("Classify(%q) = %v, want %v", tc.topic, got, tc.want) + } + }) + } +} + +func TestClassifyIsCaseSensitive(t *testing.T) { + // ONVIF topic names are case-sensitive per spec; we should not silently + // upper/lower-case. A lowercased topic must not match. + if got := Classify("tns1:videosource/motionalarm"); got != KindUnknown { + t.Errorf("Classify lowercase = %v, want KindUnknown (topics are case-sensitive)", got) + } +} From f679aab0d5b3376bcb8cef36be79d1d168a2b573 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:10:07 +0200 Subject: [PATCH 26/53] feat(event/stream): cross-reference vendor topic strings with public docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-checked each topic string against public sources before adding the rule, and inlined the citation next to the rule it supports so future maintainers can audit the table: * Hikvision motion: CellMotionDetector/Motion (Hikvision PDF on third party motion troubleshooting) plus the VideoSource/MotionAlarm fallback emitted by newer firmware. * Hikvision tamper-class scene change: VideoSource/ImageTooDark|Bright| Blurry — present in the ONVIF topic namespace; treated as Tampering for routing. * Bosch motion: VideoAnalytics/MotionAlarm (Bosch metadata/IVA PDF) — NOT VideoSource/MotionAlarm. The earlier 'tnsbosch:MotionAlarm' guess in PR #194 was wrong; Bosch uses standard tns1 namespace under VideoAnalytics. * Hanwha (Samsung Wisenet): VideoAnalytics/tnssamsung:MotionDetection, VideoAnalytics/tnssamsung:TamperingDetection, AudioAnalytics/tnssamsung:SoundDetection — confirmed via HA #66493 capture. * Avigilon: per-segment-namespaced serialisation (tns1:Device/tns1:Trigger/tns1:Relay) folded by canonicalization. Documented in Avigilon's own ONVIF subscription guide. * Object analytics: LineDetector/Crossed, FieldDetector/ObjectsInside and the MyRuleDetector container for vendor rule names (Bosch IVA, Dahua SMD) — sourced from ONVIF Analytics Service Spec v22.06. * AXIS Object Analytics: prefix match on ObjectAnalytics/ to absorb the dynamic Device1Scenario suffixes (AXIS counting-data docs). Empirical topic table cross-checked with openvideolibs/onvif-parsers (Apache-2.0), the package the Home Assistant ONVIF integration imports — referenced from the package doc-comment. Test cases now cover the verified topic for every supported vendor plus case-sensitivity and canonicalization. No code change for callers: the public API is still just Classify(topic) -> EventKind. --- event/stream/topics.go | 141 ++++++++++++++++++++++++++++++------ event/stream/topics_test.go | 108 ++++++++++++++++++++++----- 2 files changed, 206 insertions(+), 43 deletions(-) diff --git a/event/stream/topics.go b/event/stream/topics.go index 35b1bf4..8149446 100644 --- a/event/stream/topics.go +++ b/event/stream/topics.go @@ -6,10 +6,21 @@ import "strings" // to the normalized EventKind that callers should switch on. Returns // KindUnknown when no rule matches. // -// The classifier strips XML-namespace prefixes from each path segment so it -// is robust to vendor-specific namespaces like tnsaxis:, tnsbosch:, -// tnssamsung:. Matching is case-sensitive because ONVIF topic identifiers -// are case-sensitive per the spec. +// The classifier strips XML-namespace prefixes (tns1:, tnsaxis:, +// tnssamsung:, ...) from each "/"-separated segment of the topic so it is +// robust to vendor namespace variants. Matching is case-sensitive because +// ONVIF topic identifiers are case-sensitive per the spec. +// +// Sources cross-checked when building the rule set below: +// - ONVIF Topic Namespace XML +// https://www.onvif.org/onvif/ver10/topics/topicns.xml +// - ONVIF Analytics Service Spec (RuleEngine topics) +// https://www.onvif.org/specs/srv/analytics/ONVIF-VideoAnalytics-Service-Spec-v220.pdf +// - ONVIF Device IO Service Spec (DigitalInput, Relay) +// https://www.onvif.org/specs/srv/io/ONVIF-DeviceIo-Service-Spec.pdf +// - openvideolibs/onvif-parsers (Apache-2.0) — empirical topic table +// extracted from Home Assistant ONVIF integration +// https://github.com/openvideolibs/onvif-parsers func Classify(topic string) EventKind { if topic == "" { return KindUnknown @@ -23,10 +34,11 @@ func Classify(topic string) EventKind { return KindUnknown } -// canonicalizeTopic strips the XML-namespace prefix (e.g. "tns1:") from each -// "/"-separated segment of the topic. This collapses vendor variants like -// "tns1:Device/tnssamsung:DigitalInput" and the plain -// "tns1:Device/DigitalInput" form to the same canonical path. +// canonicalizeTopic strips the XML-namespace prefix (anything up to and +// including the first ':') from each "/"-separated segment. This collapses +// vendor variants like "tns1:Device/tns1:Trigger/tns1:Relay" (Avigilon +// serialisation) and "tns1:Device/Trigger/Relay" (everyone else) to a +// single matchable form. func canonicalizeTopic(topic string) string { segments := strings.Split(topic, "/") for i, seg := range segments { @@ -37,36 +49,119 @@ func canonicalizeTopic(topic string) string { return strings.Join(segments, "/") } -// topicRules is evaluated in order; first match wins. Keep the most specific -// rules first when adding new entries — e.g. "MotionDetector/Motion" must -// precede a hypothetical bare "/Motion" rule. +// topicRules is evaluated in order; first match wins. Keep more specific +// rules ahead of broader ones — e.g. "ObjectAnalytics/" must precede any +// future bare "Analytics" rule. Each rule cites the documentation that +// supports including it. var topicRules = []struct { needle string kind EventKind }{ - // Motion: covers AXIS VideoSource/MotionAlarm, ONVIF - // RuleEngine/CellMotionDetector and MotionRegionDetector, and - // vendor-namespaced MotionAlarm variants (Bosch). - {"MotionAlarm", KindMotion}, + // ---------- Motion ------------------------------------------------- + + // tns1:VideoSource/MotionAlarm — Profile S basic motion. Emitted by + // AXIS (basic VMD), Bosch, Dahua, Hikvision (newer firmware) and + // Hanwha as a fallback. Data SimpleItem: State (xsd:boolean). + // https://www.onvif.org/ver10/topics/topicns.xml + // https://developer.axis.com/vapix/network-video/event-and-action-services/ + {"VideoSource/MotionAlarm", KindMotion}, + + // tns1:VideoAnalytics/MotionAlarm — Bosch publishes motion under + // VideoAnalytics rather than VideoSource. Data: State. + // https://media.boschsecurity.com/fs/media/pb/media/partners_1/integration_tools_1/developer/bosch-metadata-and-iva-events.pdf + {"VideoAnalytics/MotionAlarm", KindMotion}, + + // tns1:VideoAnalytics/tnssamsung:MotionDetection — Hanwha/Samsung + // Wisenet vendor-namespaced motion. Data: Motion ("0"/"1"). + // https://github.com/home-assistant/core/issues/66493 + {"VideoAnalytics/MotionDetection", KindMotion}, + + // tns1:RuleEngine/CellMotionDetector/Motion — ONVIF Analytics + // standard cell-motion rule. Emitted by AXIS (VMD3+), Hikvision, + // Avigilon analytics, others. Data: IsMotion (xsd:boolean). + // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.3 + // https://www.hikvisioneurope.com/eu/portal/portal/Technical%20Materials/24%20How%20To/CCTV/How%20to%20solve%20third%20party%20camera%20motion%20detection%20issue.pdf {"CellMotionDetector/Motion", KindMotion}, + + // tns1:RuleEngine/MotionRegionDetector/Motion — AXIS-specific region + // motion rule. Data: IsMotion (xsd:boolean). + // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"MotionRegionDetector/Motion", KindMotion}, - // Tampering: ONVIF RuleEngine/TamperDetector. + // ---------- Tampering --------------------------------------------- + + // tns1:RuleEngine/TamperDetector/Tamper — standard ONVIF tamper + // rule. Data: IsTamper (xsd:boolean). + // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.5 {"TamperDetector", KindTampering}, - // Digital I/O: ONVIF Device/Trigger/{DigitalInput,Relay}. The - // canonicalization step normalizes vendor-prefixed inner segments - // (tnssamsung:DigitalInput, tns1:Relay) to the bare names. + // tns1:VideoSource/ImageTooDark|ImageTooBright|ImageTooBlurry — + // scene-change-class signals emitted by Hikvision (and some others) + // on firmwares without a TamperDetector rule. Treated as Tampering + // for the purpose of normalised event routing. + // https://www.onvif.org/ver10/topics/topicns.xml + {"VideoSource/ImageTooDark", KindTampering}, + {"VideoSource/ImageTooBright", KindTampering}, + {"VideoSource/ImageTooBlurry", KindTampering}, + + // tns1:VideoAnalytics/tnssamsung:TamperingDetection — Hanwha vendor. + // https://github.com/home-assistant/core/issues/66493 + {"VideoAnalytics/TamperingDetection", KindTampering}, + + // ---------- Digital I/O ------------------------------------------- + + // tns1:Device/Trigger/DigitalInput — standard ONVIF DeviceIO topic. + // Avigilon emits the per-segment-prefixed variant + // "tns1:Device/tns1:Trigger/tns1:DigitalInput"; canonicalization + // folds both to the same path. Data: LogicalState (xsd:boolean). + // ONVIF-DeviceIo-Service-Spec.pdf §5.2 {"Trigger/DigitalInput", KindDigitalInput}, + + // tns1:Device/Trigger/Relay — standard ONVIF DeviceIO topic. Same + // canonicalisation note as DigitalInput. Data: LogicalState. + // ONVIF-DeviceIo-Service-Spec.pdf §5.3 {"Trigger/Relay", KindDigitalOutput}, - // Object analytics: AXIS ObjectAnalytics scenarios use dynamic suffixes - // (Device1ScenarioANY, Device1Scenario1, ...), so match the path prefix. + // ---------- Object analytics -------------------------------------- + + // tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario + // — AXIS Object Analytics. The Scenario suffix is dynamic + // (Device1Scenario1, Device1ScenarioANY, ...) so we match the path + // prefix. Data: active ("0"/"1"). + // https://developer.axis.com/analytics/axis-object-analytics/how-to-guides/axis-object-analytics-counting-data/ {"ObjectAnalytics/", KindObjectDetected}, + + // tns1:RuleEngine/LineDetector/Crossed — line crossing (Hikvision, + // Bosch IVA, others). Data: ObjectId (xsd:int). + // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.4 + {"LineDetector/Crossed", KindObjectDetected}, + + // tns1:RuleEngine/FieldDetector/ObjectsInside — intrusion / region + // detector (Hikvision, Bosch, Dahua). + {"FieldDetector/ObjectsInside", KindObjectDetected}, + + // tns1:RuleEngine/MyRuleDetector/ — vendor-defined rule + // names under the ONVIF "MyRuleDetector" container. Bosch IVA and + // Dahua SMD publish HumanDetect, VehicleDetect, ObjectsInside, etc. + // here. We match the container so future rule names are picked up + // automatically. + // https://media.boschsecurity.com/fs/media/pb/media/partners_1/integration_tools_1/developer/bosch-metadata-and-iva-events.pdf + {"MyRuleDetector/", KindObjectDetected}, + + // Fallback retained for legacy ObjectsInside callers that omit the + // MyRuleDetector container. {"ObjectsInside", KindObjectDetected}, - // Audio: ONVIF AudioAnalytics/Audio/DetectedSound and AXIS - // AudioSource/TriggerLevel. + // ---------- Audio -------------------------------------------------- + + // tns1:AudioAnalytics/Audio/DetectedSound — standard ONVIF audio + // detection. Data: State (xsd:boolean). {"Audio/DetectedSound", KindAudioAlarm}, + + // tns1:AudioSource/tnsaxis:TriggerLevel — AXIS audio level alarm. + // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"AudioSource/TriggerLevel", KindAudioAlarm}, + + // tns1:AudioAnalytics/tnssamsung:SoundDetection — Hanwha vendor. + {"AudioAnalytics/SoundDetection", KindAudioAlarm}, } diff --git a/event/stream/topics_test.go b/event/stream/topics_test.go index 1477d47..58a96de 100644 --- a/event/stream/topics_test.go +++ b/event/stream/topics_test.go @@ -8,32 +8,83 @@ func TestClassifyTopic(t *testing.T) { topic string want EventKind }{ - // AXIS: motion alarm on video source. - {"axis_video_source_motion", "tns1:VideoSource/MotionAlarm", KindMotion}, - // AXIS / Hikvision / others: ONVIF cell-motion detector rule. + // --- Motion ----------------------------------------------------- + + // Profile S basic motion (AXIS basic VMD, Bosch, Dahua, + // Hikvision newer firmware, Hanwha fallback). Data: State. + {"video_source_motion_alarm", "tns1:VideoSource/MotionAlarm", KindMotion}, + + // ONVIF Analytics rule (AXIS, Hikvision standard, Avigilon + // analytics). Data: IsMotion. {"cell_motion_detector", "tns1:RuleEngine/CellMotionDetector/Motion", KindMotion}, - // AXIS region motion. + + // AXIS region rule. Data: IsMotion. {"motion_region_detector", "tns1:RuleEngine/MotionRegionDetector/Motion", KindMotion}, - // Vendor-prefixed motion (e.g. Bosch). - {"bosch_motion", "tns1:VideoAnalytics/tnsbosch:MotionAlarm", KindMotion}, - // Tampering / scene change. + + // Bosch publishes motion under VideoAnalytics (not VideoSource). + {"bosch_video_analytics_motion", "tns1:VideoAnalytics/MotionAlarm", KindMotion}, + + // Hanwha (Samsung/Wisenet) vendor-namespaced motion. + {"hanwha_samsung_motion", "tns1:VideoAnalytics/tnssamsung:MotionDetection", KindMotion}, + + // --- Tampering / scene change ---------------------------------- + + // ONVIF RuleEngine tamper rule. Data: IsTamper. {"tamper_detector", "tns1:RuleEngine/TamperDetector/Tamper", KindTampering}, - {"axis_scene_tamper", "tns1:VideoSource/ImageTooDark/ImagingService", KindUnknown}, // not classified - // Digital input — vendor-namespaced variant from agent code. + + // Hikvision uses VideoSource/Image* topics for tamper-class + // signals on firmwares without TamperDetector. + {"hikvision_image_too_dark", "tns1:VideoSource/ImageTooDark/ImagingService", KindTampering}, + {"hikvision_image_too_bright", "tns1:VideoSource/ImageTooBright/ImagingService", KindTampering}, + {"hikvision_image_too_blurry", "tns1:VideoSource/ImageTooBlurry/ImagingService", KindTampering}, + + // Hanwha vendor-namespaced tampering. + {"hanwha_tampering", "tns1:VideoAnalytics/tnssamsung:TamperingDetection", KindTampering}, + + // --- Digital input --------------------------------------------- + + // Standard ONVIF Device IO topic — same across all vendors that + // follow the spec. {"digital_input", "tns1:Device/Trigger/DigitalInput", KindDigitalInput}, - {"digital_input_samsung", "tns1:Device/tns1:Trigger/tnssamsung:DigitalInput", KindDigitalInput}, - // Digital output / relay. + + // Avigilon serialises every path segment with a namespace prefix. + {"digital_input_avigilon", "tns1:Device/tns1:Trigger/tns1:DigitalInput", KindDigitalInput}, + + // --- Digital output / relay ------------------------------------ + {"relay", "tns1:Device/Trigger/Relay", KindDigitalOutput}, {"relay_avigilon", "tns1:Device/tns1:Trigger/tns1:Relay", KindDigitalOutput}, - // Analytics object detection (AXIS object analytics, generic motion analytics). - {"object_detected", "tns1:RuleEngine/MyRuleDetector/ObjectsInside", KindObjectDetected}, - {"axis_object_analytics", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1ScenarioANY", KindObjectDetected}, - // Audio. - {"audio_alarm", "tns1:AudioAnalytics/Audio/DetectedSound", KindAudioAlarm}, - {"axis_audio", "tnsaxis:AudioSource/TriggerLevel", KindAudioAlarm}, - // Unknown — should not be force-classified. + + // --- Object analytics ------------------------------------------ + + // AXIS Object Analytics scenarios — the suffix is dynamic + // (Device1Scenario1, Device1ScenarioANY, ...). + {"axis_object_analytics_scenario_any", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1ScenarioANY", KindObjectDetected}, + {"axis_object_analytics_scenario_1", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", KindObjectDetected}, + + // Hikvision line crossing. + {"line_detector_crossed", "tns1:RuleEngine/LineDetector/Crossed", KindObjectDetected}, + + // Region / intrusion detector. + {"field_detector_objects_inside", "tns1:RuleEngine/FieldDetector/ObjectsInside", KindObjectDetected}, + + // Bosch IVA / Dahua SMD publish vendor rule names under + // MyRuleDetector. + {"my_rule_detector_human", "tns1:RuleEngine/MyRuleDetector/HumanDetect", KindObjectDetected}, + {"my_rule_detector_vehicle", "tns1:RuleEngine/MyRuleDetector/VehicleDetect", KindObjectDetected}, + {"my_rule_detector_objects_inside", "tns1:RuleEngine/MyRuleDetector/ObjectsInside", KindObjectDetected}, + + // --- Audio ----------------------------------------------------- + + {"audio_detected_sound", "tns1:AudioAnalytics/Audio/DetectedSound", KindAudioAlarm}, + {"axis_audio_trigger_level", "tns1:AudioSource/tnsaxis:TriggerLevel", KindAudioAlarm}, + {"hanwha_sound_detection", "tns1:AudioAnalytics/tnssamsung:SoundDetection", KindAudioAlarm}, + + // --- Negative cases -------------------------------------------- + {"empty", "", KindUnknown}, {"unknown_topic", "tns1:UserAlarm/IVA", KindUnknown}, + {"unrelated_recording_config", "tns1:RecordingConfig/JobState", KindUnknown}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { @@ -45,9 +96,26 @@ func TestClassifyTopic(t *testing.T) { } func TestClassifyIsCaseSensitive(t *testing.T) { - // ONVIF topic names are case-sensitive per spec; we should not silently - // upper/lower-case. A lowercased topic must not match. + // ONVIF topic identifiers are case-sensitive per the spec; a + // lowercased topic must not match a capitalised pattern. if got := Classify("tns1:videosource/motionalarm"); got != KindUnknown { t.Errorf("Classify lowercase = %v, want KindUnknown (topics are case-sensitive)", got) } } + +func TestCanonicalizeTopicStripsNamespaces(t *testing.T) { + tests := []struct { + in, want string + }{ + {"tns1:VideoSource/MotionAlarm", "VideoSource/MotionAlarm"}, + {"tns1:Device/tns1:Trigger/tns1:Relay", "Device/Trigger/Relay"}, + {"tns1:VideoAnalytics/tnssamsung:MotionDetection", "VideoAnalytics/MotionDetection"}, + {"tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", "CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1"}, + {"", ""}, + } + for _, tc := range tests { + if got := canonicalizeTopic(tc.in); got != tc.want { + t.Errorf("canonicalizeTopic(%q) = %q, want %q", tc.in, got, tc.want) + } + } +} From 4da4842f61dc195b5165bdf36e0910a42cb2b1d4 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:11:46 +0200 Subject: [PATCH 27/53] test(event/stream): adopt testify to match existing lib style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rest of github.com/kerberos-io/onvif uses stretchr/testify (assert, require) consistently — Device_test.go, event/type_test.go, media2/types_test.go, ws-discovery/networking_test.go. Migrate the two new test files in event/stream from stdlib t.Errorf to the same testify convention so the package fits in without local style variation. No production-code change; no behaviour change. --- event/stream/topics_test.go | 18 ++++++++---------- event/stream/types_test.go | 30 +++++++++--------------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/event/stream/topics_test.go b/event/stream/topics_test.go index 58a96de..482dad2 100644 --- a/event/stream/topics_test.go +++ b/event/stream/topics_test.go @@ -1,6 +1,10 @@ package stream -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" +) func TestClassifyTopic(t *testing.T) { tests := []struct { @@ -88,9 +92,7 @@ func TestClassifyTopic(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - if got := Classify(tc.topic); got != tc.want { - t.Errorf("Classify(%q) = %v, want %v", tc.topic, got, tc.want) - } + assert.Equal(t, tc.want, Classify(tc.topic), "topic=%q", tc.topic) }) } } @@ -98,9 +100,7 @@ func TestClassifyTopic(t *testing.T) { func TestClassifyIsCaseSensitive(t *testing.T) { // ONVIF topic identifiers are case-sensitive per the spec; a // lowercased topic must not match a capitalised pattern. - if got := Classify("tns1:videosource/motionalarm"); got != KindUnknown { - t.Errorf("Classify lowercase = %v, want KindUnknown (topics are case-sensitive)", got) - } + assert.Equal(t, KindUnknown, Classify("tns1:videosource/motionalarm")) } func TestCanonicalizeTopicStripsNamespaces(t *testing.T) { @@ -114,8 +114,6 @@ func TestCanonicalizeTopicStripsNamespaces(t *testing.T) { {"", ""}, } for _, tc := range tests { - if got := canonicalizeTopic(tc.in); got != tc.want { - t.Errorf("canonicalizeTopic(%q) = %q, want %q", tc.in, got, tc.want) - } + assert.Equal(t, tc.want, canonicalizeTopic(tc.in), "input=%q", tc.in) } } diff --git a/event/stream/types_test.go b/event/stream/types_test.go index 45d4069..d6e8313 100644 --- a/event/stream/types_test.go +++ b/event/stream/types_test.go @@ -3,6 +3,8 @@ package stream import ( "testing" "time" + + "github.com/stretchr/testify/assert" ) func TestEventKindString(t *testing.T) { @@ -20,9 +22,7 @@ func TestEventKindString(t *testing.T) { {EventKind(255), "EventKind(255)"}, } for _, tc := range tests { - if got := tc.kind.String(); got != tc.want { - t.Errorf("EventKind(%d).String() = %q, want %q", tc.kind, got, tc.want) - } + assert.Equal(t, tc.want, tc.kind.String()) } } @@ -37,9 +37,7 @@ func TestEventStateString(t *testing.T) { {EventState(255), "EventState(255)"}, } for _, tc := range tests { - if got := tc.state.String(); got != tc.want { - t.Errorf("EventState(%d).String() = %q, want %q", tc.state, got, tc.want) - } + assert.Equal(t, tc.want, tc.state.String()) } } @@ -55,29 +53,19 @@ func TestPropertyOperationString(t *testing.T) { {PropertyOperation(255), "PropertyOperation(255)"}, } for _, tc := range tests { - if got := tc.op.String(); got != tc.want { - t.Errorf("PropertyOperation(%d).String() = %q, want %q", tc.op, got, tc.want) - } + assert.Equal(t, tc.want, tc.op.String()) } } func TestEventZeroValue(t *testing.T) { var e Event - if e.Kind != KindUnknown { - t.Errorf("zero Event.Kind = %v, want KindUnknown", e.Kind) - } - if e.State != StateUnknown { - t.Errorf("zero Event.State = %v, want StateUnknown", e.State) - } - if !e.Timestamp.IsZero() { - t.Errorf("zero Event.Timestamp = %v, want zero time", e.Timestamp) - } + assert.Equal(t, KindUnknown, e.Kind) + assert.Equal(t, StateUnknown, e.State) + assert.True(t, e.Timestamp.IsZero()) } func TestEventTimestampPreserved(t *testing.T) { now := time.Now() e := Event{Timestamp: now} - if !e.Timestamp.Equal(now) { - t.Errorf("Event.Timestamp = %v, want %v", e.Timestamp, now) - } + assert.True(t, e.Timestamp.Equal(now)) } From da1ecf8e0a0b27ff640a0308841121b344d783a9 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:25:59 +0200 Subject: [PATCH 28/53] refactor(event/stream): address API and classifier review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel expert review of the four-commit scaffold surfaced 13 actionable items split across API design, ONVIF domain accuracy, Go idiomaticity and test rigor. This change addresses them before the Stream type lands, when the public surface is still cheap to move. API shape (hard-to-reverse before tagging) ------------------------------------------ * Rename EventKind -> Kind and EventState -> State to avoid the stream.EventKind / stream.EventState stutter when imported. * Restructure Event for non-lossy decode: - Source string and RawValue string replaced with Source/Data maps so multi-item ONVIF Source and Data lists (e.g. AXIS AOA emitting active+classType+confidence; DigitalInput carrying InputToken+ LogicalState) are preserved. - Add DeviceID so a single channel can fan in events from multiple cameras. - Add DeviceTime parsed from wsnt:UtcTime alongside the local observation Timestamp. The earlier doc-comment decision to bake-in 'drop UtcTime' was a policy disguised as an API; expose both and let callers choose. Classifier accuracy (ONVIF domain audit) ---------------------------------------- * Introduce KindImageQuality for tns1:VideoSource/ImageTooDark|Bright| Blurry. These are imaging-quality alarms that integrators route separately because they fire on sunset/dawn/condensation, not tamper. Previously mis-classified as KindTampering. * Add tns1:VideoSource/GlobalSceneChange -> KindTampering, which is the real lens-cover signal on firmwares without TamperDetector. * Anchor the TamperDetector rule to 'TamperDetector/Tamper' so a hypothetical 'TamperDetectorLog' path cannot match. * Narrow MyRuleDetector from container-match to an explicit whitelist (HumanDetect, VehicleDetect, PeopleDetect, ObjectsInside, FaceDetect). Bosch publishes Counter and Occupancy under MyRuleDetector too; those must not classify as ObjectDetected. * Add the AXIS Guard suite (MotionGuard, FenceGuard, LoiteringGuard) -> KindMotion. Common on AXIS deployments configured with these apps instead of basic VMD. * Drop the bogus Device1ScenarioANY test fixture; AOA uses numeric scenarios (Device1Scenario1, Device1Scenario2). The 'ANY' suffix was a borrow from the older Guard suite's Camera1ProfileANY pattern. * Document the edge-trigger semantics of LineDetector/Crossed in the rule comment so decoder consumers do not expect a State boolean. Tests ----- * String tests now use t.Run subtests so failures name the case. * TestKindStringsAreUnique guards against accidental String() aliasing when adding new kinds. * TestEventFieldAssignmentRoundTrip exercises the new field set including DeviceID, Source/Data maps and DeviceTime. * Canonicalisation table now covers: double slash, colon-only segment, trailing colon, multi-colon-in-segment, leading/trailing slash, no-colon passthrough. Locks the actual behaviour so future refactors see regressions. * False-positive negatives: Counter and Occupancy under MyRuleDetector, AudioEncoderConfiguration, RelayFailure, DigitalInputConfiguration, TamperDetectorLog, MotionRecording/Started — all assert KindUnknown. * TestClassifyRuleOrder_ObjectAnalyticsBeforeGenericObjects pins the ordering invariant called out by the architect reviewer. Documentation ------------- * doc.go trimmed so it does not advertise NewStream / Events / Errors / Close before those identifiers exist — the godoc reader will no longer see dead names. Re-expanded when the Stream type lands. Deferred to the Stream commit ----------------------------- * PropertyUnknown vs PropertyUnset disambiguation — kept as PropertyUnknown for now with a clarified doc comment; revisit when the decoder needs to distinguish 'absent on wire' from 'unparseable'. * Classifier pluggability (WithClassifier option) — meaningful only once there is a Stream; revisit at that commit. --- event/stream/doc.go | 16 +++--- event/stream/topics.go | 95 ++++++++++++++++++++++----------- event/stream/topics_test.go | 103 +++++++++++++++++++++--------------- event/stream/types.go | 90 +++++++++++++++++++++---------- event/stream/types_test.go | 99 ++++++++++++++++++++++++---------- 5 files changed, 267 insertions(+), 136 deletions(-) diff --git a/event/stream/doc.go b/event/stream/doc.go index ba0f79b..0223ae1 100644 --- a/event/stream/doc.go +++ b/event/stream/doc.go @@ -1,13 +1,13 @@ -// Package stream provides a long-running, channel-based consumer for ONVIF -// device events. It hides the SOAP/XML, pull-point lifecycle, renewal and -// vendor-specific topic conventions behind a typed Event stream. +// Package stream will provide a long-running, channel-based consumer for +// ONVIF device events. It is meant to hide the SOAP/XML, pull-point +// subscription lifecycle, subscription renewal and vendor-specific topic +// conventions behind a typed Event stream. // -// A Stream is created with NewStream and yields decoded Event values on the -// channel returned by Events. Non-fatal errors (transient SOAP failures that -// the stream recovers from) are surfaced on Errors. The Stream is stopped by -// cancelling the context passed to NewStream or by calling Close. +// This file lays down the value types (Kind, State, PropertyOperation, +// Event) and the topic Classifier. The Stream type, its NewStream +// constructor and the Events/Errors channels land in follow-up changes. // // The package classifies vendor-specific topic strings (AXIS, Hikvision, -// Avigilon, Hanwha, Bosch, Dahua) into a small set of normalized EventKind +// Avigilon, Hanwha, Bosch, Dahua) into a small set of normalized Kind // values so callers do not need to special-case device manufacturers. package stream diff --git a/event/stream/topics.go b/event/stream/topics.go index 8149446..9aba5be 100644 --- a/event/stream/topics.go +++ b/event/stream/topics.go @@ -3,7 +3,7 @@ package stream import "strings" // Classify maps an ONVIF topic string (e.g. "tns1:VideoSource/MotionAlarm") -// to the normalized EventKind that callers should switch on. Returns +// to the normalized Kind that callers should switch on. Returns // KindUnknown when no rule matches. // // The classifier strips XML-namespace prefixes (tns1:, tnsaxis:, @@ -21,7 +21,7 @@ import "strings" // - openvideolibs/onvif-parsers (Apache-2.0) — empirical topic table // extracted from Home Assistant ONVIF integration // https://github.com/openvideolibs/onvif-parsers -func Classify(topic string) EventKind { +func Classify(topic string) Kind { if topic == "" { return KindUnknown } @@ -39,6 +39,10 @@ func Classify(topic string) EventKind { // vendor variants like "tns1:Device/tns1:Trigger/tns1:Relay" (Avigilon // serialisation) and "tns1:Device/Trigger/Relay" (everyone else) to a // single matchable form. +// +// A segment that is only a prefix (e.g. "tns1:") canonicalizes to the +// empty string. Multiple colons in one segment are not expected in real +// ONVIF topics; the first colon wins. func canonicalizeTopic(topic string) string { segments := strings.Split(topic, "/") for i, seg := range segments { @@ -51,11 +55,21 @@ func canonicalizeTopic(topic string) string { // topicRules is evaluated in order; first match wins. Keep more specific // rules ahead of broader ones — e.g. "ObjectAnalytics/" must precede any -// future bare "Analytics" rule. Each rule cites the documentation that -// supports including it. +// future bare "Analytics" rule, and "MyRuleDetector/HumanDetect" must +// precede a hypothetical broader "MyRuleDetector" entry. Each rule cites +// the documentation that supports including it. +// +// Substring matching is intentional so vendor-specific path prefixes +// outside the standard tns1: namespace (e.g. +// tnsaxis:CameraApplicationPlatform/...) still match. +// +// Note on edge-triggered topics: tns1:RuleEngine/LineDetector/Crossed +// carries an ObjectId rather than a State boolean. Consumers of Crossed +// must not expect a level-triggered Active/Inactive semantic — the Stream +// decoder will leave State as StateUnknown for these. var topicRules = []struct { needle string - kind EventKind + kind Kind }{ // ---------- Motion ------------------------------------------------- @@ -88,69 +102,90 @@ var topicRules = []struct { // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"MotionRegionDetector/Motion", KindMotion}, + // AXIS Guard suite — vendor analytics apps that fire motion-like + // events with CameraProfile suffixes. Treated as motion so + // they can drive motion-triggered recording on cameras configured + // with these apps instead of basic VMD. + // https://developer.axis.com/vapix/applications/motion-guard + {"CameraApplicationPlatform/MotionGuard/", KindMotion}, + {"CameraApplicationPlatform/FenceGuard/", KindMotion}, + {"CameraApplicationPlatform/LoiteringGuard/", KindMotion}, + // ---------- Tampering --------------------------------------------- // tns1:RuleEngine/TamperDetector/Tamper — standard ONVIF tamper - // rule. Data: IsTamper (xsd:boolean). + // rule. Data: IsTamper (xsd:boolean). Anchored on the rule-name + // segment so "TamperDetectorLog" (hypothetical) does not match. // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.5 - {"TamperDetector", KindTampering}, + {"TamperDetector/Tamper", KindTampering}, - // tns1:VideoSource/ImageTooDark|ImageTooBright|ImageTooBlurry — - // scene-change-class signals emitted by Hikvision (and some others) - // on firmwares without a TamperDetector rule. Treated as Tampering - // for the purpose of normalised event routing. + // tns1:VideoSource/GlobalSceneChange/ImagingService — Hikvision (and + // others) emit this on real lens-cover / scene substitution. This is + // the proper tamper signal on firmwares without TamperDetector. // https://www.onvif.org/ver10/topics/topicns.xml - {"VideoSource/ImageTooDark", KindTampering}, - {"VideoSource/ImageTooBright", KindTampering}, - {"VideoSource/ImageTooBlurry", KindTampering}, + {"GlobalSceneChange", KindTampering}, // tns1:VideoAnalytics/tnssamsung:TamperingDetection — Hanwha vendor. // https://github.com/home-assistant/core/issues/66493 {"VideoAnalytics/TamperingDetection", KindTampering}, + // ---------- Image quality ----------------------------------------- + + // tns1:VideoSource/ImageTooDark|ImageTooBright|ImageTooBlurry — + // imaging-quality alarms. Integrators (Milestone, Genetec, Frigate) + // route these separately from tamper because they fire on legitimate + // sunset/dawn/condensation transitions, not on actual interference. + // https://www.onvif.org/ver10/topics/topicns.xml + {"VideoSource/ImageTooDark", KindImageQuality}, + {"VideoSource/ImageTooBright", KindImageQuality}, + {"VideoSource/ImageTooBlurry", KindImageQuality}, + // ---------- Digital I/O ------------------------------------------- // tns1:Device/Trigger/DigitalInput — standard ONVIF DeviceIO topic. // Avigilon emits the per-segment-prefixed variant // "tns1:Device/tns1:Trigger/tns1:DigitalInput"; canonicalization - // folds both to the same path. Data: LogicalState (xsd:boolean). + // folds both to the same path. Data: LogicalState (xsd:boolean), + // Source: InputToken. // ONVIF-DeviceIo-Service-Spec.pdf §5.2 {"Trigger/DigitalInput", KindDigitalInput}, // tns1:Device/Trigger/Relay — standard ONVIF DeviceIO topic. Same - // canonicalisation note as DigitalInput. Data: LogicalState. + // canonicalisation note as DigitalInput. Data: LogicalState, + // Source: RelayToken. // ONVIF-DeviceIo-Service-Spec.pdf §5.3 {"Trigger/Relay", KindDigitalOutput}, // ---------- Object analytics -------------------------------------- // tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario - // — AXIS Object Analytics. The Scenario suffix is dynamic - // (Device1Scenario1, Device1ScenarioANY, ...) so we match the path - // prefix. Data: active ("0"/"1"). + // — AXIS Object Analytics. Scenario suffixes are numeric per the + // AOA configuration (Device1Scenario1, Device1Scenario2, ...). Data: + // active ("0"/"1") plus classType / confidence when configured. // https://developer.axis.com/analytics/axis-object-analytics/how-to-guides/axis-object-analytics-counting-data/ {"ObjectAnalytics/", KindObjectDetected}, // tns1:RuleEngine/LineDetector/Crossed — line crossing (Hikvision, - // Bosch IVA, others). Data: ObjectId (xsd:int). + // Bosch IVA, others). Data: ObjectId (xsd:int); edge-triggered, no + // State boolean. // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.4 {"LineDetector/Crossed", KindObjectDetected}, // tns1:RuleEngine/FieldDetector/ObjectsInside — intrusion / region - // detector (Hikvision, Bosch, Dahua). + // detector (Hikvision, Bosch, Dahua). Data: IsInside (xsd:boolean). {"FieldDetector/ObjectsInside", KindObjectDetected}, // tns1:RuleEngine/MyRuleDetector/ — vendor-defined rule - // names under the ONVIF "MyRuleDetector" container. Bosch IVA and - // Dahua SMD publish HumanDetect, VehicleDetect, ObjectsInside, etc. - // here. We match the container so future rule names are picked up - // automatically. + // names under the ONVIF MyRuleDetector container. We whitelist + // object-class rules emitted by Bosch IVA, Dahua SMD and Hikvision + // AcuSense so non-object rules under the same container (Bosch + // Counter, Occupancy) do not get mis-classified. // https://media.boschsecurity.com/fs/media/pb/media/partners_1/integration_tools_1/developer/bosch-metadata-and-iva-events.pdf - {"MyRuleDetector/", KindObjectDetected}, - - // Fallback retained for legacy ObjectsInside callers that omit the - // MyRuleDetector container. - {"ObjectsInside", KindObjectDetected}, + {"MyRuleDetector/HumanDetect", KindObjectDetected}, + {"MyRuleDetector/VehicleDetect", KindObjectDetected}, + {"MyRuleDetector/PeopleDetect", KindObjectDetected}, + {"MyRuleDetector/ObjectsInside", KindObjectDetected}, + {"MyRuleDetector/FaceDetect", KindObjectDetected}, // ---------- Audio -------------------------------------------------- diff --git a/event/stream/topics_test.go b/event/stream/topics_test.go index 482dad2..2bc2a34 100644 --- a/event/stream/topics_test.go +++ b/event/stream/topics_test.go @@ -10,72 +10,58 @@ func TestClassifyTopic(t *testing.T) { tests := []struct { name string topic string - want EventKind + want Kind }{ // --- Motion ----------------------------------------------------- - // Profile S basic motion (AXIS basic VMD, Bosch, Dahua, - // Hikvision newer firmware, Hanwha fallback). Data: State. {"video_source_motion_alarm", "tns1:VideoSource/MotionAlarm", KindMotion}, - - // ONVIF Analytics rule (AXIS, Hikvision standard, Avigilon - // analytics). Data: IsMotion. {"cell_motion_detector", "tns1:RuleEngine/CellMotionDetector/Motion", KindMotion}, - - // AXIS region rule. Data: IsMotion. {"motion_region_detector", "tns1:RuleEngine/MotionRegionDetector/Motion", KindMotion}, - - // Bosch publishes motion under VideoAnalytics (not VideoSource). {"bosch_video_analytics_motion", "tns1:VideoAnalytics/MotionAlarm", KindMotion}, - - // Hanwha (Samsung/Wisenet) vendor-namespaced motion. {"hanwha_samsung_motion", "tns1:VideoAnalytics/tnssamsung:MotionDetection", KindMotion}, - // --- Tampering / scene change ---------------------------------- + // AXIS Guard suite — vendor analytics apps. + {"axis_motion_guard", "tnsaxis:CameraApplicationPlatform/MotionGuard/Camera1ProfileANY", KindMotion}, + {"axis_fence_guard", "tnsaxis:CameraApplicationPlatform/FenceGuard/Camera1ProfileANY", KindMotion}, + {"axis_loitering_guard", "tnsaxis:CameraApplicationPlatform/LoiteringGuard/Camera1ProfileANY", KindMotion}, + + // --- Tampering -------------------------------------------------- - // ONVIF RuleEngine tamper rule. Data: IsTamper. {"tamper_detector", "tns1:RuleEngine/TamperDetector/Tamper", KindTampering}, - - // Hikvision uses VideoSource/Image* topics for tamper-class - // signals on firmwares without TamperDetector. - {"hikvision_image_too_dark", "tns1:VideoSource/ImageTooDark/ImagingService", KindTampering}, - {"hikvision_image_too_bright", "tns1:VideoSource/ImageTooBright/ImagingService", KindTampering}, - {"hikvision_image_too_blurry", "tns1:VideoSource/ImageTooBlurry/ImagingService", KindTampering}, - - // Hanwha vendor-namespaced tampering. + {"global_scene_change", "tns1:VideoSource/GlobalSceneChange/ImagingService", KindTampering}, {"hanwha_tampering", "tns1:VideoAnalytics/tnssamsung:TamperingDetection", KindTampering}, + // --- Image quality (separated from Tampering) ------------------ + + {"image_too_dark", "tns1:VideoSource/ImageTooDark/ImagingService", KindImageQuality}, + {"image_too_bright", "tns1:VideoSource/ImageTooBright/ImagingService", KindImageQuality}, + {"image_too_blurry", "tns1:VideoSource/ImageTooBlurry/ImagingService", KindImageQuality}, + // --- Digital input --------------------------------------------- - // Standard ONVIF Device IO topic — same across all vendors that - // follow the spec. {"digital_input", "tns1:Device/Trigger/DigitalInput", KindDigitalInput}, - - // Avigilon serialises every path segment with a namespace prefix. {"digital_input_avigilon", "tns1:Device/tns1:Trigger/tns1:DigitalInput", KindDigitalInput}, - // --- Digital output / relay ------------------------------------ + // --- Digital output -------------------------------------------- {"relay", "tns1:Device/Trigger/Relay", KindDigitalOutput}, {"relay_avigilon", "tns1:Device/tns1:Trigger/tns1:Relay", KindDigitalOutput}, // --- Object analytics ------------------------------------------ - // AXIS Object Analytics scenarios — the suffix is dynamic - // (Device1Scenario1, Device1ScenarioANY, ...). - {"axis_object_analytics_scenario_any", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1ScenarioANY", KindObjectDetected}, + // AXIS Object Analytics uses numeric scenario suffixes. {"axis_object_analytics_scenario_1", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", KindObjectDetected}, + {"axis_object_analytics_scenario_2", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario2", KindObjectDetected}, - // Hikvision line crossing. + // Standard rule-engine analytics topics. {"line_detector_crossed", "tns1:RuleEngine/LineDetector/Crossed", KindObjectDetected}, - - // Region / intrusion detector. {"field_detector_objects_inside", "tns1:RuleEngine/FieldDetector/ObjectsInside", KindObjectDetected}, - // Bosch IVA / Dahua SMD publish vendor rule names under - // MyRuleDetector. + // Whitelisted MyRuleDetector sub-rules. {"my_rule_detector_human", "tns1:RuleEngine/MyRuleDetector/HumanDetect", KindObjectDetected}, {"my_rule_detector_vehicle", "tns1:RuleEngine/MyRuleDetector/VehicleDetect", KindObjectDetected}, + {"my_rule_detector_people", "tns1:RuleEngine/MyRuleDetector/PeopleDetect", KindObjectDetected}, + {"my_rule_detector_face", "tns1:RuleEngine/MyRuleDetector/FaceDetect", KindObjectDetected}, {"my_rule_detector_objects_inside", "tns1:RuleEngine/MyRuleDetector/ObjectsInside", KindObjectDetected}, // --- Audio ----------------------------------------------------- @@ -89,6 +75,19 @@ func TestClassifyTopic(t *testing.T) { {"empty", "", KindUnknown}, {"unknown_topic", "tns1:UserAlarm/IVA", KindUnknown}, {"unrelated_recording_config", "tns1:RecordingConfig/JobState", KindUnknown}, + + // MyRuleDetector overmatch guard — Bosch publishes counter and + // occupancy under the same container and these must not be + // classified as object detection. + {"my_rule_detector_counter_not_object", "tns1:RuleEngine/MyRuleDetector/Counter", KindUnknown}, + {"my_rule_detector_occupancy_not_object", "tns1:RuleEngine/MyRuleDetector/Occupancy", KindUnknown}, + + // Substring guards. + {"motion_recording_not_motion", "tns1:Recording/MotionRecording/Started", KindUnknown}, + {"audio_encoder_config_not_audio_alarm", "tns1:Configuration/AudioEncoderConfiguration", KindUnknown}, + {"relay_failure_not_digital_output", "tns1:Device/HardwareFailure/RelayFailure", KindUnknown}, + {"digital_input_config_not_digital_input", "tns1:Device/IO/DigitalInputConfiguration", KindUnknown}, + {"tamper_detector_log_not_tampering", "tns1:Device/Diagnostics/TamperDetectorLog", KindUnknown}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { @@ -105,15 +104,35 @@ func TestClassifyIsCaseSensitive(t *testing.T) { func TestCanonicalizeTopicStripsNamespaces(t *testing.T) { tests := []struct { - in, want string + name string + in string + want string }{ - {"tns1:VideoSource/MotionAlarm", "VideoSource/MotionAlarm"}, - {"tns1:Device/tns1:Trigger/tns1:Relay", "Device/Trigger/Relay"}, - {"tns1:VideoAnalytics/tnssamsung:MotionDetection", "VideoAnalytics/MotionDetection"}, - {"tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", "CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1"}, - {"", ""}, + {"single_namespace", "tns1:VideoSource/MotionAlarm", "VideoSource/MotionAlarm"}, + {"per_segment_namespace", "tns1:Device/tns1:Trigger/tns1:Relay", "Device/Trigger/Relay"}, + {"vendor_namespace_inner", "tns1:VideoAnalytics/tnssamsung:MotionDetection", "VideoAnalytics/MotionDetection"}, + {"axis_outer_namespace", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", "CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1"}, + {"empty", "", ""}, + {"no_colon_passthrough", "Foo/Bar", "Foo/Bar"}, + {"double_slash_keeps_empty_segment", "tns1://Foo", "//Foo"}, + {"colon_only_segment_collapses_to_empty", "tns1:/Foo", "/Foo"}, + {"trailing_colon_segment", "tns1:", ""}, + {"multi_colon_takes_first", "tns1:Foo:Bar/Baz", "Foo:Bar/Baz"}, + {"leading_slash_kept", "/tns1:Foo", "/Foo"}, + {"trailing_slash_kept", "tns1:Foo/", "Foo/"}, } for _, tc := range tests { - assert.Equal(t, tc.want, canonicalizeTopic(tc.in), "input=%q", tc.in) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.want, canonicalizeTopic(tc.in), "input=%q", tc.in) + }) } } + +func TestClassifyRuleOrder_ObjectAnalyticsBeforeGenericObjects(t *testing.T) { + // Locks the invariant that the prefix rule "ObjectAnalytics/" is + // matched before the broader "ObjectsInside" rule. Without this + // ordering, AXIS AOA topics that contain neither would still classify + // correctly via the ObjectAnalytics/ rule; we encode the dependency. + topic := "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1" + assert.Equal(t, KindObjectDetected, Classify(topic)) +} diff --git a/event/stream/types.go b/event/stream/types.go index 3d0bae1..fcc3612 100644 --- a/event/stream/types.go +++ b/event/stream/types.go @@ -5,19 +5,25 @@ import ( "time" ) -// EventKind is the normalized category of an ONVIF event, independent of the +// Kind is the normalized category of an ONVIF event, independent of the // camera vendor's topic naming. -type EventKind uint8 +type Kind uint8 const ( // KindUnknown is the zero value; used when a topic does not match any // known classification. - KindUnknown EventKind = iota + KindUnknown Kind = iota // KindMotion covers motion detection from any vendor (e.g. AXIS // VideoSource/MotionAlarm, Hikvision RuleEngine/CellMotionDetector). KindMotion - // KindTampering covers camera tampering / scene change alarms. + // KindTampering covers true tamper alarms (lens cover, scene + // substitution). Imaging-quality alarms map to KindImageQuality. KindTampering + // KindImageQuality covers VideoSource imaging alarms such as + // ImageTooDark, ImageTooBright and ImageTooBlurry. Most integrators + // treat these separately from tamper because they fire on legitimate + // sunset/dawn/condensation transitions. + KindImageQuality // KindDigitalInput covers external sensor inputs wired to the camera. KindDigitalInput // KindDigitalOutput covers relay output state changes on the camera. @@ -30,7 +36,7 @@ const ( ) // String implements fmt.Stringer. -func (k EventKind) String() string { +func (k Kind) String() string { switch k { case KindUnknown: return "Unknown" @@ -38,6 +44,8 @@ func (k EventKind) String() string { return "Motion" case KindTampering: return "Tampering" + case KindImageQuality: + return "ImageQuality" case KindDigitalInput: return "DigitalInput" case KindDigitalOutput: @@ -47,23 +55,24 @@ func (k EventKind) String() string { case KindAudioAlarm: return "AudioAlarm" default: - return fmt.Sprintf("EventKind(%d)", uint8(k)) + return fmt.Sprintf("Kind(%d)", uint8(k)) } } -// EventState is the active/inactive state carried by an event. Most ONVIF -// alarms are boolean (e.g. IsMotion=true/false); StateUnknown is used when -// the value cannot be parsed. -type EventState uint8 +// State is the active/inactive level carried by a boolean ONVIF property +// event (e.g. IsMotion=true/false). StateUnknown is used both when the +// value cannot be parsed and when the topic is edge-triggered and carries +// no boolean state (e.g. LineDetector/Crossed). +type State uint8 const ( - StateUnknown EventState = iota + StateUnknown State = iota StateActive StateInactive ) // String implements fmt.Stringer. -func (s EventState) String() string { +func (s State) String() string { switch s { case StateUnknown: return "Unknown" @@ -72,13 +81,15 @@ func (s EventState) String() string { case StateInactive: return "Inactive" default: - return fmt.Sprintf("EventState(%d)", uint8(s)) + return fmt.Sprintf("State(%d)", uint8(s)) } } // PropertyOperation mirrors the ONVIF wsnt:PropertyOperation attribute and // indicates whether a message is the first sighting of a property -// (Initialized), a transition (Changed) or the property going away (Deleted). +// (Initialized), a transition (Changed) or the property going away +// (Deleted). PropertyUnknown is used both when the attribute is absent on +// the wire (the spec allows it) and when the value is unrecognised. type PropertyOperation uint8 const ( @@ -107,24 +118,45 @@ func (p PropertyOperation) String() string { // Event is a single normalized notification from an ONVIF device. // // Kind, State and Operation are the normalized fields most callers should -// switch on. Topic, RawValue and Source preserve the original ONVIF data so -// callers can do further inspection or logging without re-parsing SOAP. +// switch on. Topic, Source and Data preserve the original ONVIF data so +// callers can inspect the wire form without re-parsing SOAP. +// +// Source and Data are maps from ONVIF SimpleItem Name to Value because +// notifications can carry multiple items: AXIS Object Analytics for +// example emits active, classType and confidence in the same Data list, +// and standard DigitalInput notifications carry both InputToken in Source +// and LogicalState in Data. type Event struct { // Kind is the normalized event category. - Kind EventKind - // State is the active/inactive value carried by the event. - State EventState - // Operation is the ONVIF property lifecycle (Initialized/Changed/Deleted). + Kind Kind + // State is the active/inactive value carried by a boolean event. + // StateUnknown for edge-triggered events (LineDetector/Crossed) that + // carry no boolean property. + State State + // Operation is the ONVIF property lifecycle + // (Initialized/Changed/Deleted). Operation PropertyOperation - // Source identifies the channel, input, or rule that produced the event - // (taken from the Source SimpleItem in the notification). - Source string - // Topic is the raw ONVIF topic string, e.g. tns1:VideoSource/MotionAlarm. + // DeviceID identifies the camera that produced the event. Set by the + // Stream from the caller-supplied identifier so a single channel can + // fan in events from multiple devices. + DeviceID string + // Source is the ONVIF Source SimpleItem map (e.g. InputToken, + // VideoSourceConfigurationToken, Rule). Empty when the notification + // has no Source section. + Source map[string]string + // Data is the ONVIF Data SimpleItem map (e.g. IsMotion, LogicalState, + // active, classType). Empty when the notification has no Data + // section. + Data map[string]string + // Topic is the raw ONVIF topic string, e.g. + // tns1:VideoSource/MotionAlarm. Topic string - // RawValue is the unparsed Data SimpleItem value (e.g. "true", "1", - // "active") so callers can read non-boolean values when needed. - RawValue string - // Timestamp is when the stream observed the event locally. The ONVIF - // UtcTime is not used because clocks on many cameras drift. + // Timestamp is when the stream observed the event locally. Timestamp time.Time + // DeviceTime is the camera-reported wsnt:UtcTime, when present and + // parseable. Zero if the camera omits the attribute or sends an + // unparseable value. Many cameras have drifting clocks; prefer + // Timestamp for ordering and DeviceTime only for forensics or + // cross-camera correlation when caller manages NTP. + DeviceTime time.Time } diff --git a/event/stream/types_test.go b/event/stream/types_test.go index d6e8313..ed2399d 100644 --- a/event/stream/types_test.go +++ b/event/stream/types_test.go @@ -7,53 +7,73 @@ import ( "github.com/stretchr/testify/assert" ) -func TestEventKindString(t *testing.T) { +func TestKindString(t *testing.T) { tests := []struct { - kind EventKind + name string + kind Kind want string }{ - {KindUnknown, "Unknown"}, - {KindMotion, "Motion"}, - {KindTampering, "Tampering"}, - {KindDigitalInput, "DigitalInput"}, - {KindDigitalOutput, "DigitalOutput"}, - {KindObjectDetected, "ObjectDetected"}, - {KindAudioAlarm, "AudioAlarm"}, - {EventKind(255), "EventKind(255)"}, + {"unknown", KindUnknown, "Unknown"}, + {"motion", KindMotion, "Motion"}, + {"tampering", KindTampering, "Tampering"}, + {"image_quality", KindImageQuality, "ImageQuality"}, + {"digital_input", KindDigitalInput, "DigitalInput"}, + {"digital_output", KindDigitalOutput, "DigitalOutput"}, + {"object_detected", KindObjectDetected, "ObjectDetected"}, + {"audio_alarm", KindAudioAlarm, "AudioAlarm"}, + {"out_of_range", Kind(255), "Kind(255)"}, } for _, tc := range tests { - assert.Equal(t, tc.want, tc.kind.String()) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.want, tc.kind.String()) + }) } } -func TestEventStateString(t *testing.T) { +func TestKindStringsAreUnique(t *testing.T) { + seen := map[string]Kind{} + for k := KindUnknown; k <= KindAudioAlarm; k++ { + s := k.String() + prev, dup := seen[s] + assert.False(t, dup, "duplicate String %q for Kind(%d) and Kind(%d)", s, prev, k) + seen[s] = k + } +} + +func TestStateString(t *testing.T) { tests := []struct { - state EventState + name string + state State want string }{ - {StateUnknown, "Unknown"}, - {StateActive, "Active"}, - {StateInactive, "Inactive"}, - {EventState(255), "EventState(255)"}, + {"unknown", StateUnknown, "Unknown"}, + {"active", StateActive, "Active"}, + {"inactive", StateInactive, "Inactive"}, + {"out_of_range", State(255), "State(255)"}, } for _, tc := range tests { - assert.Equal(t, tc.want, tc.state.String()) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.want, tc.state.String()) + }) } } func TestPropertyOperationString(t *testing.T) { tests := []struct { + name string op PropertyOperation want string }{ - {PropertyUnknown, "Unknown"}, - {PropertyInitialized, "Initialized"}, - {PropertyChanged, "Changed"}, - {PropertyDeleted, "Deleted"}, - {PropertyOperation(255), "PropertyOperation(255)"}, + {"unknown", PropertyUnknown, "Unknown"}, + {"initialized", PropertyInitialized, "Initialized"}, + {"changed", PropertyChanged, "Changed"}, + {"deleted", PropertyDeleted, "Deleted"}, + {"out_of_range", PropertyOperation(255), "PropertyOperation(255)"}, } for _, tc := range tests { - assert.Equal(t, tc.want, tc.op.String()) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.want, tc.op.String()) + }) } } @@ -61,11 +81,36 @@ func TestEventZeroValue(t *testing.T) { var e Event assert.Equal(t, KindUnknown, e.Kind) assert.Equal(t, StateUnknown, e.State) + assert.Equal(t, PropertyUnknown, e.Operation) + assert.Empty(t, e.DeviceID) + assert.Nil(t, e.Source) + assert.Nil(t, e.Data) + assert.Empty(t, e.Topic) assert.True(t, e.Timestamp.IsZero()) + assert.True(t, e.DeviceTime.IsZero()) } -func TestEventTimestampPreserved(t *testing.T) { - now := time.Now() - e := Event{Timestamp: now} +func TestEventFieldAssignmentRoundTrip(t *testing.T) { + now := time.Now().UTC() + deviceTime := now.Add(-2 * time.Second) + e := Event{ + Kind: KindMotion, + State: StateActive, + Operation: PropertyChanged, + DeviceID: "axis-camera-01", + Source: map[string]string{"InputToken": "DI1"}, + Data: map[string]string{"LogicalState": "true"}, + Topic: "tns1:Device/Trigger/DigitalInput", + Timestamp: now, + DeviceTime: deviceTime, + } + assert.Equal(t, KindMotion, e.Kind) + assert.Equal(t, StateActive, e.State) + assert.Equal(t, PropertyChanged, e.Operation) + assert.Equal(t, "axis-camera-01", e.DeviceID) + assert.Equal(t, "DI1", e.Source["InputToken"]) + assert.Equal(t, "true", e.Data["LogicalState"]) + assert.Equal(t, "tns1:Device/Trigger/DigitalInput", e.Topic) assert.True(t, e.Timestamp.Equal(now)) + assert.True(t, e.DeviceTime.Equal(deviceTime)) } From b461ec8ded3e08db0dea37804b77c6043ca188a6 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:30:50 +0200 Subject: [PATCH 29/53] feat(event/stream): decode NotificationMessage into normalized Event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the Decode entry point that converts the ONVIF wire form into the package's typed Event. The agent (and any other consumer) no longer has to walk NotificationMessage.Message.Message.Data.SimpleItem chains and hand-special-case per-vendor data item names. Decoding rules -------------- * Topic -> Kind via the verified Classify table. * PropertyOperation parses Initialized/Changed/Deleted; absent or unrecognised -> PropertyUnknown (the attribute is optional per WS-Notification). * UtcTime parses RFC3339Nano first, RFC3339 second, normalised to UTC. Absent or unparseable -> DeviceTime is zero. Camera clocks drift; the type doc already steers callers to prefer Timestamp. * State extraction scans Data items in order for the first boolean-like value (true/false/1/0/active/inactive, case-insensitive). This handles every vendor data item in the verified table — IsMotion, State, IsTamper, LogicalState, active, Motion, triggered, SoundDetection, TamperingDetection — without a per-kind switch. * Edge-triggered topics (LineDetector/Crossed with only ObjectId) yield StateUnknown, matching the topic-rule doc note. * Source and Data are full ONVIF SimpleItem name->value maps so callers retain multi-item info (AXIS AOA active+classType+confidence, digital I/O InputToken+LogicalState, analytics VideoSourceConfigurationToken+ Rule). Empty notifications yield nil maps, matching the Event zero-value contract from types_test.go. * Topic, Source and Data are always populated even when Kind is KindUnknown, so consumers can log/route unclassified events. Tests cover the AXIS motion happy path, the inactive case, the Hanwha numeric-string variant, the Avigilon 'active' literal, multi-item AOA decode, the LineDetector edge-trigger semantic, unknown-topic wire preservation, every PropertyOperation literal, RFC3339 with sub-second and timezone offsets, and case-insensitive State extraction. --- event/stream/decode.go | 104 +++++++++++++++ event/stream/decode_test.go | 249 ++++++++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 event/stream/decode.go create mode 100644 event/stream/decode_test.go diff --git a/event/stream/decode.go b/event/stream/decode.go new file mode 100644 index 0000000..1d96941 --- /dev/null +++ b/event/stream/decode.go @@ -0,0 +1,104 @@ +package stream + +import ( + "strings" + "time" + + "github.com/kerberos-io/onvif/event" +) + +// Decode converts a single ONVIF NotificationMessage into the package's +// normalized Event representation. +// +// deviceID is supplied by the caller because the message itself does not +// identify the originating camera. observedAt is recorded verbatim as +// Event.Timestamp; the camera-reported wsnt:UtcTime attribute (when +// present and parseable) populates Event.DeviceTime. +// +// When the Topic does not match any classifier rule the returned Event +// has Kind == KindUnknown but Source, Data and Topic are still populated +// so consumers can fall back to inspecting the wire form. +func Decode(msg event.NotificationMessage, deviceID string, observedAt time.Time) Event { + topic := string(msg.Topic.TopicKinds) + desc := msg.Message.Message + return Event{ + Kind: Classify(topic), + State: extractState(desc.Data.SimpleItem), + Operation: parsePropertyOperation(string(desc.PropertyOperation)), + DeviceID: deviceID, + Source: simpleItemsToMap(desc.Source.SimpleItem), + Data: simpleItemsToMap(desc.Data.SimpleItem), + Topic: topic, + Timestamp: observedAt, + DeviceTime: parseDeviceTime(string(desc.UtcTime)), + } +} + +// simpleItemsToMap collapses ONVIF SimpleItem lists to a Name->Value map. +// Returns nil for an empty list so empty notifications do not allocate +// and match the Event zero-value contract. +func simpleItemsToMap(items []event.SimpleItem) map[string]string { + if len(items) == 0 { + return nil + } + m := make(map[string]string, len(items)) + for _, it := range items { + m[string(it.Name)] = string(it.Value) + } + return m +} + +// extractState scans Data items for a boolean-like value and returns the +// first one as a State. Returns StateUnknown when no item parses — this +// is the correct outcome for edge-triggered topics such as +// LineDetector/Crossed whose Data carries only an ObjectId. +// +// Iteration order over the original []SimpleItem is preserved so the +// behaviour stays deterministic per notification. (Map iteration is not +// involved; simpleItemsToMap is a separate path.) +func extractState(items []event.SimpleItem) State { + for _, it := range items { + switch strings.ToLower(strings.TrimSpace(string(it.Value))) { + case "true", "1", "active": + return StateActive + case "false", "0", "inactive": + return StateInactive + } + } + return StateUnknown +} + +// parsePropertyOperation parses the wsnt:PropertyOperation attribute. +// The attribute is optional per WS-Notification; an empty or unrecognised +// value yields PropertyUnknown. +func parsePropertyOperation(s string) PropertyOperation { + switch s { + case "Initialized": + return PropertyInitialized + case "Changed": + return PropertyChanged + case "Deleted": + return PropertyDeleted + default: + return PropertyUnknown + } +} + +// parseDeviceTime parses the wsnt:UtcTime attribute, returning the zero +// time when the attribute is absent or unparseable. The result is +// normalised to UTC so equality comparisons across timezones work. +// +// xsd:dateTime in ONVIF messages is RFC 3339 in practice; we try +// time.RFC3339Nano first (covers sub-second precision) and fall back to +// time.RFC3339 for cameras that drop the fractional part. +func parseDeviceTime(s string) time.Time { + if s == "" { + return time.Time{} + } + for _, layout := range []string{time.RFC3339Nano, time.RFC3339} { + if t, err := time.Parse(layout, s); err == nil { + return t.UTC() + } + } + return time.Time{} +} diff --git a/event/stream/decode_test.go b/event/stream/decode_test.go new file mode 100644 index 0000000..edce4c8 --- /dev/null +++ b/event/stream/decode_test.go @@ -0,0 +1,249 @@ +package stream + +import ( + "testing" + "time" + + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" + "github.com/stretchr/testify/assert" +) + +// msg builds a NotificationMessage from the topic and a (PropertyOperation, +// UtcTime, source items, data items) tuple so tests stay short and intent +// is visible at the call site. +func msg(topic, propOp, utcTime string, source, data map[string]string) event.NotificationMessage { + toItems := func(m map[string]string) []event.SimpleItem { + if len(m) == 0 { + return nil + } + items := make([]event.SimpleItem, 0, len(m)) + for k, v := range m { + items = append(items, event.SimpleItem{ + Name: xsd.AnyType(k), + Value: xsd.AnyType(v), + }) + } + return items + } + return event.NotificationMessage{ + Topic: event.Topic{TopicKinds: xsd.String(topic)}, + Message: event.MessageBody{ + Message: event.MessageDescription{ + PropertyOperation: xsd.AnyType(propOp), + UtcTime: xsd.AnyType(utcTime), + Source: event.Source{SimpleItem: toItems(source)}, + Data: event.Data{SimpleItem: toItems(data)}, + }, + }, + } +} + +func TestDecode_MotionActive(t *testing.T) { + observedAt := time.Date(2026, 5, 21, 10, 30, 1, 0, time.UTC) + in := msg( + "tns1:RuleEngine/CellMotionDetector/Motion", + "Changed", + "2026-05-21T10:30:00Z", + map[string]string{ + "VideoSourceConfigurationToken": "VideoSourceConfigToken0", + "Rule": "MyMotionRule", + }, + map[string]string{"IsMotion": "true"}, + ) + + ev := Decode(in, "axis-cam-01", observedAt) + + assert.Equal(t, KindMotion, ev.Kind) + assert.Equal(t, StateActive, ev.State) + assert.Equal(t, PropertyChanged, ev.Operation) + assert.Equal(t, "axis-cam-01", ev.DeviceID) + assert.Equal(t, "VideoSourceConfigToken0", ev.Source["VideoSourceConfigurationToken"]) + assert.Equal(t, "MyMotionRule", ev.Source["Rule"]) + assert.Equal(t, "true", ev.Data["IsMotion"]) + assert.Equal(t, "tns1:RuleEngine/CellMotionDetector/Motion", ev.Topic) + assert.True(t, ev.Timestamp.Equal(observedAt)) + assert.Equal(t, time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC), ev.DeviceTime) +} + +func TestDecode_MotionInactive(t *testing.T) { + in := msg( + "tns1:VideoSource/MotionAlarm", + "Changed", + "", + nil, + map[string]string{"State": "false"}, + ) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, KindMotion, ev.Kind) + assert.Equal(t, StateInactive, ev.State) +} + +func TestDecode_HanwhaNumericMotionValue(t *testing.T) { + // Hanwha emits xsd:string values "0"/"1" instead of xsd:boolean. + in := msg( + "tns1:VideoAnalytics/tnssamsung:MotionDetection", + "Changed", + "", + nil, + map[string]string{"Motion": "1"}, + ) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, KindMotion, ev.Kind) + assert.Equal(t, StateActive, ev.State) +} + +func TestDecode_AvigilonActiveLiteral(t *testing.T) { + // Avigilon and a handful of older firmwares emit "active"/"inactive" + // as the Data value rather than a boolean. + in := msg( + "tns1:Device/tns1:Trigger/tns1:Relay", + "Changed", + "", + map[string]string{"RelayToken": "Relay-1"}, + map[string]string{"LogicalState": "active"}, + ) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, KindDigitalOutput, ev.Kind) + assert.Equal(t, StateActive, ev.State) + assert.Equal(t, "Relay-1", ev.Source["RelayToken"]) +} + +func TestDecode_AxisObjectAnalyticsMultiItem(t *testing.T) { + // AOA emits active + classType + confidence in the same Data list. + // The decoder must preserve every item; State picks the first + // boolean-like value, which is 'active'. + in := msg( + "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario1", + "Changed", + "", + map[string]string{"Source": "device1Scene1"}, + map[string]string{ + "active": "1", + "classType": "Human", + "confidence": "92", + }, + ) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, KindObjectDetected, ev.Kind) + assert.Equal(t, StateActive, ev.State) + assert.Equal(t, "Human", ev.Data["classType"]) + assert.Equal(t, "92", ev.Data["confidence"]) + assert.Equal(t, "1", ev.Data["active"]) +} + +func TestDecode_LineDetectorCrossedHasNoState(t *testing.T) { + // Edge-triggered topic — Data carries ObjectId, not a boolean. State + // must remain Unknown so consumers do not misread it as level-Active. + in := msg( + "tns1:RuleEngine/LineDetector/Crossed", + "Changed", + "", + map[string]string{"VideoSourceConfigurationToken": "vsct0", "Rule": "LineRule"}, + map[string]string{"ObjectId": "42"}, + ) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, KindObjectDetected, ev.Kind) + assert.Equal(t, StateUnknown, ev.State) + assert.Equal(t, "42", ev.Data["ObjectId"]) +} + +func TestDecode_UnknownTopicStillPreservesWireData(t *testing.T) { + // Kind unknown does not mean discard: consumers may want to log or + // route on the raw topic when classification misses. + in := msg( + "tns1:UserAlarm/IVA", + "", + "", + nil, + map[string]string{"Custom": "true"}, + ) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, KindUnknown, ev.Kind) + assert.Equal(t, "tns1:UserAlarm/IVA", ev.Topic) + assert.Equal(t, "true", ev.Data["Custom"]) +} + +func TestDecode_PropertyOperationVariants(t *testing.T) { + tests := []struct { + name string + in string + want PropertyOperation + }{ + {"initialized", "Initialized", PropertyInitialized}, + {"changed", "Changed", PropertyChanged}, + {"deleted", "Deleted", PropertyDeleted}, + {"absent", "", PropertyUnknown}, + {"unrecognised", "Bogus", PropertyUnknown}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", tc.in, "", nil, nil) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, tc.want, ev.Operation) + }) + } +} + +func TestDecode_DeviceTimeParsing(t *testing.T) { + tests := []struct { + name string + in string + want time.Time + }{ + {"rfc3339_utc", "2026-05-21T10:30:00Z", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, + {"rfc3339_with_offset", "2026-05-21T12:30:00+02:00", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, + {"rfc3339_subsecond", "2026-05-21T10:30:00.500Z", time.Date(2026, 5, 21, 10, 30, 0, 500_000_000, time.UTC)}, + {"absent", "", time.Time{}}, + {"unparseable", "not-a-date", time.Time{}}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", tc.in, nil, nil) + ev := Decode(in, "dev", time.Now()) + if tc.want.IsZero() { + assert.True(t, ev.DeviceTime.IsZero(), "DeviceTime=%v", ev.DeviceTime) + } else { + assert.True(t, ev.DeviceTime.Equal(tc.want), "got=%v want=%v", ev.DeviceTime, tc.want) + } + }) + } +} + +func TestDecode_EmptySourceAndDataYieldNilMaps(t *testing.T) { + // Matches the zero-value contract in types_test.go: callers can + // safely len() and index into Source/Data without nil-checking, but + // we do not allocate an empty map for empty notifications. + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", nil, nil) + ev := Decode(in, "dev", time.Now()) + assert.Nil(t, ev.Source) + assert.Nil(t, ev.Data) +} + +func TestDecode_StateValueIsCaseInsensitive(t *testing.T) { + tests := []struct { + name string + value string + want State + }{ + {"true_lower", "true", StateActive}, + {"true_upper", "TRUE", StateActive}, + {"true_mixed", "True", StateActive}, + {"false_lower", "false", StateInactive}, + {"false_mixed", "False", StateInactive}, + {"active_mixed", "Active", StateActive}, + {"inactive_mixed", "Inactive", StateInactive}, + {"one", "1", StateActive}, + {"zero", "0", StateInactive}, + {"empty", "", StateUnknown}, + {"nonsense", "maybe", StateUnknown}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", + nil, map[string]string{"State": tc.value}) + ev := Decode(in, "dev", time.Now()) + assert.Equal(t, tc.want, ev.State) + }) + } +} From fb05c31d7dc540c1165dba403d22181e477b5735 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:35:34 +0200 Subject: [PATCH 30/53] feat(event/stream): add Stream with pull-point lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces Stream, the typed event consumer the package will eventually present to callers, plus the caller seam needed to test it without hitting a real camera. Stream owns one ONVIF pull-point subscription end-to-end: * CreatePullPointSubscription on construction so authentication and reachability problems surface synchronously from NewStream rather than landing on the Errors channel after the goroutine starts. * Background pull loop calls PullMessages against the SubscriptionReference Address returned by Create. Each NotificationMessage is fed through Decode and pushed on the Events channel, with context cancellation honoured between every step so a Close cannot get stuck behind a long-server-side-wait pull. * Errors during a pull are surfaced on a separate Errors channel using a non-blocking send; a stalled consumer drops older errors instead of blocking the loop. The loop sleeps briefly (ctx-aware) and retries — automatic subscription recreation lands in the reconnect-on-error commit. * Close cancels the context, waits for the run goroutine to exit, Unsubscribes the pull point and closes Events/Errors. sync.Once keeps it idempotent. Design seams ------------ * caller interface (CallMethod + SendSoap) abstracts *onvif.Device so tests can substitute fakeCaller without an HTTP server. deviceCaller is the production adapter; newStream takes the interface, NewStream takes the concrete *onvif.Device. The same shape lets a future commit add WithClassifier / WithClock / WithCaller options if the architect reviewer's pluggable-classifier note becomes urgent. * now func() time.Time is a Stream field so a future clock-injecting test (renew timing, observed-at determinism) can swap it. * unmarshalNode keys on the local XML name, sidestepping namespace matching since SOAP envelopes from different vendors prefix the PullMessagesResponse and CreatePullPointSubscriptionResponse with arbitrary tev:/tev1:/... bindings. This is the same trick the agent's getXMLNode used; lifting it here lets the agent eventually drop its copy. Options and defaults -------------------- PullTimeout 5s, MessageLimit 10, InitialTermination 60s, BufferSize 16 match what the existing agent code uses. TopicFilter defaults to empty so AXIS cameras work out of the box — the verified topic table is intentionally the routing layer, not a server-side filter, because the agent will frequently want digital I/O and motion on the same stream. Tests cover the create-then-pull-then-close happy path, that pulls target the SubscriptionReference Address (not the device endpoint), construction failure on CreatePullPoint error, context-cancel exits the loop cleanly with channels closed, transient pull errors land on Errors without stopping decode of subsequent good messages, idempotent Close, and Options default values. -race clean. --- event/stream/stream.go | 353 ++++++++++++++++++++++++++++++++++++ event/stream/stream_test.go | 342 ++++++++++++++++++++++++++++++++++ 2 files changed, 695 insertions(+) create mode 100644 event/stream/stream.go create mode 100644 event/stream/stream_test.go diff --git a/event/stream/stream.go b/event/stream/stream.go new file mode 100644 index 0000000..8ed8a44 --- /dev/null +++ b/event/stream/stream.go @@ -0,0 +1,353 @@ +package stream + +import ( + "bytes" + "context" + "encoding/xml" + "errors" + "fmt" + "io" + "net/http" + "strconv" + "sync" + "time" + + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" +) + +// Options configures a Stream. The zero value is usable; defaultOptions +// fills in production-sensible defaults for any unset field. +type Options struct { + // DeviceID identifies the camera in emitted Events. Recommended so a + // single channel can fan in multiple cameras. Empty is allowed. + DeviceID string + // TopicFilter is the raw ONVIF ConcreteSet TopicExpression filter + // passed to CreatePullPointSubscription. The empty string means no + // filter — required for AXIS, accepted by every other vendor we + // support. Callers should normally leave this empty and rely on + // Classify for routing. + TopicFilter string + // PullTimeout is the server-side wait time in each PullMessages call + // (xsd:duration). The camera returns early when messages are + // available; otherwise it returns empty after this timeout. Default: + // 5s. + PullTimeout time.Duration + // MessageLimit caps the number of NotificationMessage entries + // returned per PullMessages call. Default: 10. + MessageLimit int + // InitialTermination is the requested subscription lifetime passed + // to CreatePullPointSubscription. The renew loop (added in a later + // commit) will refresh well before this expires. Default: 60s. + InitialTermination time.Duration + // BufferSize is the buffer size of the Events and Errors channels. + // Larger buffers absorb consumer hiccups at the cost of memory. + // Default: 16. + BufferSize int +} + +func defaultOptions() Options { + return Options{ + PullTimeout: 5 * time.Second, + MessageLimit: 10, + InitialTermination: 60 * time.Second, + BufferSize: 16, + } +} + +func (o Options) withDefaults() Options { + d := defaultOptions() + if o.PullTimeout > 0 { + d.PullTimeout = o.PullTimeout + } + if o.MessageLimit > 0 { + d.MessageLimit = o.MessageLimit + } + if o.InitialTermination > 0 { + d.InitialTermination = o.InitialTermination + } + if o.BufferSize > 0 { + d.BufferSize = o.BufferSize + } + d.DeviceID = o.DeviceID + d.TopicFilter = o.TopicFilter + return d +} + +// caller is the subset of *onvif.Device the Stream depends on. Tests +// substitute a fake; production code uses the device adapter. +type caller interface { + CallMethod(method any) (*http.Response, error) + SendSoap(endpoint, body string) (*http.Response, error) +} + +type deviceCaller struct{ dev *onvif.Device } + +func (d deviceCaller) CallMethod(m any) (*http.Response, error) { + return d.dev.CallMethod(m) +} + +func (d deviceCaller) SendSoap(endpoint, body string) (*http.Response, error) { + return d.dev.SendSoap(endpoint, body) +} + +// Stream owns a single ONVIF pull-point subscription and surfaces the +// decoded notifications on a typed channel. Close stops the background +// goroutine and unsubscribes from the camera. +// +// A Stream is safe for concurrent use by Close from any goroutine while +// readers consume Events / Errors; Close is idempotent. +type Stream struct { + caller caller + opts Options + pullPoint string + + events chan Event + errors chan error + + cancel context.CancelFunc + done chan struct{} + + closeOnce sync.Once + closeErr error + + // now is overridable in tests to make timestamps deterministic. + now func() time.Time +} + +// NewStream creates a Stream against an ONVIF device. It performs the +// CreatePullPointSubscription call synchronously so connectivity and +// authentication problems surface immediately as an error rather than +// landing on the Errors channel later. The background pull loop starts +// before NewStream returns. +// +// The returned Stream stops when ctx is cancelled or when Close is +// called. +func NewStream(ctx context.Context, dev *onvif.Device, opts Options) (*Stream, error) { + return newStream(ctx, deviceCaller{dev: dev}, opts) +} + +func newStream(ctx context.Context, c caller, opts Options) (*Stream, error) { + opts = opts.withDefaults() + addr, err := createPullPoint(c, opts) + if err != nil { + return nil, fmt.Errorf("create pull point subscription: %w", err) + } + runCtx, cancel := context.WithCancel(ctx) + s := &Stream{ + caller: c, + opts: opts, + pullPoint: addr, + events: make(chan Event, opts.BufferSize), + errors: make(chan error, opts.BufferSize), + cancel: cancel, + done: make(chan struct{}), + now: time.Now, + } + go s.run(runCtx) + return s, nil +} + +// Events returns the channel of decoded notifications. The channel is +// closed when the Stream stops. +func (s *Stream) Events() <-chan Event { return s.events } + +// Errors returns the channel of non-fatal errors encountered while +// pulling. Sends are non-blocking, so consumers that fall behind drop +// older errors. The channel is closed when the Stream stops. +func (s *Stream) Errors() <-chan error { return s.errors } + +// Close stops the background goroutine, waits for it to exit, and +// unsubscribes from the camera. Subsequent calls are no-ops. +func (s *Stream) Close() error { + s.closeOnce.Do(func() { + s.cancel() + <-s.done + // Unsubscribe is best-effort: if the camera is unreachable + // the subscription will expire on its own at + // InitialTermination + Renew interval anyway. + if err := unsubscribePullPoint(s.caller, s.pullPoint); err != nil { + s.closeErr = fmt.Errorf("unsubscribe pull point: %w", err) + } + }) + return s.closeErr +} + +func (s *Stream) run(ctx context.Context) { + defer close(s.done) + defer close(s.events) + defer close(s.errors) + + for { + if ctx.Err() != nil { + return + } + msgs, err := pullMessages(s.caller, s.pullPoint, s.opts) + if err != nil { + s.surfaceError(err) + // Brief backoff before retrying; reconnect-on-error + // lands in a follow-up commit and replaces this with + // proper subscription recreation. + if !sleepCtx(ctx, time.Second) { + return + } + continue + } + observedAt := s.now() + for _, m := range msgs { + ev := Decode(m, s.opts.DeviceID, observedAt) + select { + case <-ctx.Done(): + return + case s.events <- ev: + } + } + } +} + +// surfaceError sends err on the errors channel non-blockingly so a +// stalled consumer cannot block the pull loop. +func (s *Stream) surfaceError(err error) { + select { + case s.errors <- err: + default: + } +} + +// sleepCtx blocks for d or until ctx is cancelled. Returns true if d +// elapsed, false if ctx was cancelled. +func sleepCtx(ctx context.Context, d time.Duration) bool { + t := time.NewTimer(d) + defer t.Stop() + select { + case <-ctx.Done(): + return false + case <-t.C: + return true + } +} + +// --- SOAP helpers (unexported) ---------------------------------------- + +func createPullPoint(c caller, opts Options) (string, error) { + term := xsd.String(durationToXSD(opts.InitialTermination)) + req := event.CreatePullPointSubscription{InitialTerminationTime: &term} + if opts.TopicFilter != "" { + req.Filter = &event.FilterType{ + TopicExpression: &event.TopicExpressionType{ + Dialect: xsd.String("http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"), + TopicKinds: xsd.String(opts.TopicFilter), + }, + } + } + resp, err := c.CallMethod(req) + if err != nil { + return "", err + } + body, err := readClose(resp) + if err != nil { + return "", err + } + var decoded event.CreatePullPointSubscriptionResponse + if err := unmarshalNode(body, "CreatePullPointSubscriptionResponse", &decoded); err != nil { + return "", err + } + addr := string(decoded.SubscriptionReference.Address) + if addr == "" { + return "", errors.New("CreatePullPointSubscription response has empty SubscriptionReference Address") + } + return addr, nil +} + +func pullMessages(c caller, endpoint string, opts Options) ([]event.NotificationMessage, error) { + req := event.PullMessages{ + Timeout: xsd.Duration(durationToXSD(opts.PullTimeout)), + MessageLimit: xsd.Int(opts.MessageLimit), + } + body, err := xml.Marshal(req) + if err != nil { + return nil, fmt.Errorf("marshal PullMessages: %w", err) + } + resp, err := c.SendSoap(endpoint, string(body)) + if err != nil { + return nil, err + } + respBody, err := readClose(resp) + if err != nil { + return nil, err + } + var decoded event.PullMessagesResponse + if err := unmarshalNode(respBody, "PullMessagesResponse", &decoded); err != nil { + return nil, err + } + return decoded.NotificationMessage, nil +} + +func unsubscribePullPoint(c caller, endpoint string) error { + if endpoint == "" { + return nil + } + body, err := xml.Marshal(event.Unsubscribe{}) + if err != nil { + return fmt.Errorf("marshal Unsubscribe: %w", err) + } + resp, err := c.SendSoap(endpoint, string(body)) + if err != nil { + return err + } + _, err = readClose(resp) + return err +} + +func readClose(resp *http.Response) (string, error) { + if resp == nil || resp.Body == nil { + return "", errors.New("nil HTTP response") + } + defer resp.Body.Close() + b, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("read response body: %w", err) + } + return string(b), nil +} + +// unmarshalNode finds the first XML start element with the given local +// name and decodes it into out. ONVIF SOAP responses come wrapped in an +// envelope with multiple namespace prefixes; this helper sidesteps +// namespace matching by keying on local name only. +func unmarshalNode(body, localName string, out any) error { + dec := xml.NewDecoder(bytes.NewBufferString(body)) + for { + tok, err := dec.Token() + if err != nil { + if errors.Is(err, io.EOF) { + return fmt.Errorf("ONVIF response missing %s element", localName) + } + return fmt.Errorf("scan ONVIF response: %w", err) + } + start, ok := tok.(xml.StartElement) + if !ok { + continue + } + if start.Name.Local != localName { + continue + } + if err := dec.DecodeElement(out, &start); err != nil { + return fmt.Errorf("decode %s: %w", localName, err) + } + return nil + } +} + +// durationToXSD formats a Go time.Duration as an xsd:duration string in +// PTnS form. Second precision is sufficient — ONVIF cameras do not +// honour sub-second pull timeouts and intermediate routers may round in +// any case. +func durationToXSD(d time.Duration) string { + secs := int(d.Round(time.Second).Seconds()) + if secs <= 0 { + secs = 1 + } + return "PT" + strconv.Itoa(secs) + "S" +} diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go new file mode 100644 index 0000000..698d07e --- /dev/null +++ b/event/stream/stream_test.go @@ -0,0 +1,342 @@ +package stream + +import ( + "context" + "errors" + "io" + "net/http" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --- fakeCaller -------------------------------------------------------- + +// fakeCaller is a test double for the caller interface. Each method +// returns the next queued response; when the queue is exhausted it falls +// back to a default response so the indefinite pull loop does not +// require tests to enumerate every call. +type fakeCaller struct { + mu sync.Mutex + callMethodResps []fakeResp + sendSoapResps []fakeResp + defaultSendSoap fakeResp + defaultCall fakeResp + callMethodCalls []any + sendSoapCalls [][2]string +} + +type fakeResp struct { + body string + err error +} + +func newFakeCaller() *fakeCaller { + return &fakeCaller{ + // Default: indefinite empty pulls, indefinite OK unsubscribes. + defaultSendSoap: fakeResp{body: pullMessagesResp()}, + defaultCall: fakeResp{err: errors.New("fakeCaller: no default CallMethod response")}, + } +} + +func (f *fakeCaller) queueCallMethod(body string, err error) { + f.mu.Lock() + defer f.mu.Unlock() + f.callMethodResps = append(f.callMethodResps, fakeResp{body: body, err: err}) +} + +func (f *fakeCaller) queueSendSoap(body string, err error) { + f.mu.Lock() + defer f.mu.Unlock() + f.sendSoapResps = append(f.sendSoapResps, fakeResp{body: body, err: err}) +} + +func (f *fakeCaller) CallMethod(m any) (*http.Response, error) { + f.mu.Lock() + defer f.mu.Unlock() + f.callMethodCalls = append(f.callMethodCalls, m) + r := f.defaultCall + if len(f.callMethodResps) > 0 { + r = f.callMethodResps[0] + f.callMethodResps = f.callMethodResps[1:] + } + if r.err != nil { + return nil, r.err + } + return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, nil +} + +func (f *fakeCaller) SendSoap(endpoint, body string) (*http.Response, error) { + f.mu.Lock() + defer f.mu.Unlock() + f.sendSoapCalls = append(f.sendSoapCalls, [2]string{endpoint, body}) + r := f.defaultSendSoap + if len(f.sendSoapResps) > 0 { + r = f.sendSoapResps[0] + f.sendSoapResps = f.sendSoapResps[1:] + } + if r.err != nil { + return nil, r.err + } + return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, nil +} + +func (f *fakeCaller) sendSoapCallCount() int { + f.mu.Lock() + defer f.mu.Unlock() + return len(f.sendSoapCalls) +} + +// --- fixture SOAP envelopes ------------------------------------------- + +// createPullPointResp is the minimal SOAP envelope the lib's existing +// xml.Decoder + getXMLNode path can extract a pull-point address from. +const createPullPointResp = ` + + + + + http://camera.local/onvif/Events/PullSub_1 + + 2026-05-21T10:30:00Z + 2026-05-21T10:31:00Z + + +` + +func pullMessagesResp(messages ...string) string { + return ` + + + + 2026-05-21T10:30:05Z + 2026-05-21T10:31:05Z + ` + strings.Join(messages, "\n") + ` + + +` +} + +func motionMsg(value string) string { + return ` + tns1:RuleEngine/CellMotionDetector/Motion + + + + + + + + + + +` +} + +const unsubscribeResp = ` + + + + +` + +// --- helpers ----------------------------------------------------------- + +// receive waits up to d for an event on ch, failing the test if none +// arrives. +func receive(t *testing.T, ch <-chan Event, d time.Duration) Event { + t.Helper() + select { + case ev, ok := <-ch: + if !ok { + t.Fatalf("event channel closed before receiving") + } + return ev + case <-time.After(d): + t.Fatalf("timed out waiting for event after %s", d) + } + return Event{} // unreachable +} + +// --- tests ------------------------------------------------------------- + +func TestNewStream_CreatesPullPointAtConstruction(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // Queue an empty pull so the run loop can spin without exploding. + fc.queueSendSoap(pullMessagesResp(), nil) + fc.queueSendSoap(unsubscribeResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + require.NoError(t, err) + require.NotNil(t, s) + require.NoError(t, s.Close()) + + // CreatePullPointSubscription was called exactly once. + fc.mu.Lock() + defer fc.mu.Unlock() + require.Len(t, fc.callMethodCalls, 1, "expected one CallMethod call (CreatePullPointSubscription)") +} + +func TestStream_DeliversDecodedEvents(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) + // Provide subsequent empty pulls so the loop doesn't starve before Close. + fc.queueSendSoap(pullMessagesResp(), nil) + fc.queueSendSoap(pullMessagesResp(), nil) + fc.queueSendSoap(unsubscribeResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + require.NoError(t, err) + defer s.Close() + + ev := receive(t, s.Events(), 2*time.Second) + assert.Equal(t, KindMotion, ev.Kind) + assert.Equal(t, StateActive, ev.State) + assert.Equal(t, "cam-1", ev.DeviceID) + assert.Equal(t, "tns1:RuleEngine/CellMotionDetector/Motion", ev.Topic) + assert.Equal(t, "VSC0", ev.Source["VideoSourceConfigurationToken"]) + assert.Equal(t, "true", ev.Data["IsMotion"]) +} + +func TestStream_PullsAgainstSubscriptionAddress(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueSendSoap(pullMessagesResp(), nil) + fc.queueSendSoap(pullMessagesResp(), nil) + fc.queueSendSoap(unsubscribeResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + require.NoError(t, err) + + // Wait until at least one pull happened, then close. + for i := 0; i < 50 && fc.sendSoapCallCount() == 0; i++ { + time.Sleep(10 * time.Millisecond) + } + require.NoError(t, s.Close()) + + fc.mu.Lock() + defer fc.mu.Unlock() + require.NotEmpty(t, fc.sendSoapCalls, "expected at least one PullMessages SendSoap call") + endpoint := fc.sendSoapCalls[0][0] + assert.Equal(t, "http://camera.local/onvif/Events/PullSub_1", endpoint, + "PullMessages must target the SubscriptionReference Address returned by CreatePullPoint") + // Last call (Close) should target the same endpoint with an Unsubscribe body. + last := fc.sendSoapCalls[len(fc.sendSoapCalls)-1] + assert.Equal(t, "http://camera.local/onvif/Events/PullSub_1", last[0]) + assert.Contains(t, last[1], "Unsubscribe") +} + +func TestNewStream_ReturnsErrorWhenCreatePullPointFails(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod("", errors.New("network down")) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + assert.Error(t, err) + assert.Nil(t, s) +} + +func TestStream_ClosedContextStopsRunLoop(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // Many empty pulls so the loop is hot when we cancel. + for i := 0; i < 20; i++ { + fc.queueSendSoap(pullMessagesResp(), nil) + } + fc.queueSendSoap(unsubscribeResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + require.NoError(t, err) + + // Wait for at least one pull. + for i := 0; i < 50 && fc.sendSoapCallCount() == 0; i++ { + time.Sleep(10 * time.Millisecond) + } + cancel() + + // Close should still complete cleanly; the goroutine must drain. + require.NoError(t, s.Close()) + + // Events channel must close so consumers can range-loop safely. + select { + case _, ok := <-s.Events(): + assert.False(t, ok, "Events channel should be closed after Close()") + case <-time.After(time.Second): + t.Fatal("Events channel was not closed within 1s") + } +} + +func TestStream_PullErrorSurfacedOnErrorsChannel(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueSendSoap("", errors.New("transient pull failure")) + // Then a clean pull so the loop keeps running. + fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) + fc.queueSendSoap(pullMessagesResp(), nil) + fc.queueSendSoap(unsubscribeResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + require.NoError(t, err) + defer s.Close() + + select { + case e := <-s.Errors(): + assert.Contains(t, e.Error(), "transient pull failure") + case <-time.After(2 * time.Second): + t.Fatal("expected an error on the Errors channel") + } + // After the transient failure the loop continued and decoded. + ev := receive(t, s.Events(), 2*time.Second) + assert.Equal(t, KindMotion, ev.Kind) +} + +func TestStream_OptionsApplyDefaults(t *testing.T) { + o := defaultOptions() + assert.Equal(t, 5*time.Second, o.PullTimeout) + assert.Equal(t, 10, o.MessageLimit) + assert.Equal(t, 60*time.Second, o.InitialTermination) + assert.Equal(t, 16, o.BufferSize) +} + +func TestStream_DoesNotPanicOnPullExitingDuringClose(t *testing.T) { + // Regression guard: Close should not race with the run goroutine + // in a way that double-closes the events/errors channels. + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + for i := 0; i < 5; i++ { + fc.queueSendSoap(pullMessagesResp(), nil) + } + fc.queueSendSoap(unsubscribeResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{DeviceID: "cam-1"}) + require.NoError(t, err) + assert.NotPanics(t, func() { + require.NoError(t, s.Close()) + // Double-close should be a no-op, not a panic. + _ = s.Close() + }) +} From 9b5b6261137a2959907a6770e9aa85dc49899ca1 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:37:33 +0200 Subject: [PATCH 31/53] feat(event/stream): renew subscription before termination expires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a background renew loop alongside the pull loop. ONVIF pull-point subscriptions expire at the InitialTerminationTime supplied to Create; without periodic Renew calls the camera silently drops the subscription and subsequent pulls start returning empty messages — the shape the existing agent's heartbeat code in cloud/Cloud.go has been papering over by occasionally recreating subscriptions. Design ------ * New Options.RenewMargin (default 10s) — how far before InitialTermination expiry the renew fires. Smaller margins mean fewer SOAP round-trips; larger margins tolerate slow networks. With default 60s termination + 10s margin we renew every 50s, which is in line with what production NVRs (Milestone, Genetec) use. * The renew loop runs in a separate goroutine sharing ctx with the pull loop. WaitGroup synchronisation in run() ensures both have exited before close()-of-channels happens, so a renew in flight during Close() cannot send on a closed Errors channel. * Pathological config (RenewMargin >= InitialTermination) falls back to renewing at termination/2 rather than busy-looping or never renewing. * renewPullPoint sends a wsnt:Renew SOAP against the SubscriptionRef Address with the same InitialTermination duration; renew errors surface on Errors non-blockingly, identically to pull errors. Tests use very short termination/margin (80-100ms / 10ms) so a single test run observes multiple renews within ~500ms, and assert that renew calls target the SubscriptionReference endpoint (not the device endpoint). -race clean. --- event/stream/renew_test.go | 134 +++++++++++++++++++++++++++++++++++++ event/stream/stream.go | 70 +++++++++++++++++-- event/stream/topics.go | 2 +- 3 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 event/stream/renew_test.go diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go new file mode 100644 index 0000000..20e2146 --- /dev/null +++ b/event/stream/renew_test.go @@ -0,0 +1,134 @@ +package stream + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// countSendSoapMatching counts how many recorded SendSoap calls have a +// body containing needle. Safe to call concurrently with the run loop. +func countSendSoapMatching(fc *fakeCaller, needle string) int { + fc.mu.Lock() + defer fc.mu.Unlock() + n := 0 + for _, c := range fc.sendSoapCalls { + if strings.Contains(c[1], needle) { + n++ + } + } + return n +} + +func TestStream_RenewsSubscriptionBeforeExpiry(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // 100 ms termination with 10 ms margin -> renew every ~90 ms. + s, err := newStream(ctx, fc, Options{ + DeviceID: "cam-1", + InitialTermination: 100 * time.Millisecond, + RenewMargin: 10 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(500 * time.Millisecond) + var renewCount int + for time.Now().Before(deadline) { + renewCount = countSendSoapMatching(fc, "Renew") + if renewCount >= 1 { + break + } + time.Sleep(10 * time.Millisecond) + } + assert.GreaterOrEqual(t, renewCount, 1, "expected at least one Renew SendSoap call within 500ms") +} + +func TestStream_RenewSendsToSubscriptionEndpoint(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + s, err := newStream(ctx, fc, Options{ + InitialTermination: 80 * time.Millisecond, + RenewMargin: 10 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(500 * time.Millisecond) + for time.Now().Before(deadline) { + if countSendSoapMatching(fc, "Renew") >= 1 { + break + } + time.Sleep(10 * time.Millisecond) + } + + fc.mu.Lock() + defer fc.mu.Unlock() + var renewEndpoint string + for _, c := range fc.sendSoapCalls { + if strings.Contains(c[1], "Renew") { + renewEndpoint = c[0] + break + } + } + require.NotEmpty(t, renewEndpoint, "no Renew call found") + assert.Equal(t, "http://camera.local/onvif/Events/PullSub_1", renewEndpoint, + "Renew must target the SubscriptionReference Address") +} + +func TestStream_RenewMarginAppliesDefault(t *testing.T) { + o := defaultOptions() + assert.Equal(t, 10*time.Second, o.RenewMargin) +} + +func TestStream_RenewErrorSurfacedOnErrorsChannel(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // Defaults return empty pulls indefinitely so the pull loop is clean. + // Override defaultSendSoap on the fly to return a Renew error for + // any body that looks like a Renew. We do that by tagging the + // default response with an err, then resetting after capturing one. + // Simpler: just queue several explicit Renew-error responses; the + // fake's queue is consumed in FIFO and the pull body never matches + // 'Renew', so queued errors will land on the renew call only if + // queued before any pulls. To bias the order we drain via a custom + // default. + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errInjected{}} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + s, err := newStream(ctx, fc, Options{ + InitialTermination: 80 * time.Millisecond, + RenewMargin: 10 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + select { + case e := <-s.Errors(): + assert.Contains(t, e.Error(), "injected") + case <-time.After(time.Second): + t.Fatal("expected an error on Errors channel from failing Renew/pull") + } +} + +// errInjected is a sentinel error type so the test message has a stable +// substring without depending on a wrapped string match. +type errInjected struct{} + +func (errInjected) Error() string { return "injected fake error" } diff --git a/event/stream/stream.go b/event/stream/stream.go index 8ed8a44..102f88e 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -38,9 +38,13 @@ type Options struct { // returned per PullMessages call. Default: 10. MessageLimit int // InitialTermination is the requested subscription lifetime passed - // to CreatePullPointSubscription. The renew loop (added in a later - // commit) will refresh well before this expires. Default: 60s. + // to CreatePullPointSubscription. The renew loop refreshes well + // before this expires. Default: 60s. InitialTermination time.Duration + // RenewMargin is how long before InitialTermination expiry the + // renew loop fires. Larger margins tolerate slower networks at the + // cost of more renew SOAP calls. Default: 10s. + RenewMargin time.Duration // BufferSize is the buffer size of the Events and Errors channels. // Larger buffers absorb consumer hiccups at the cost of memory. // Default: 16. @@ -52,6 +56,7 @@ func defaultOptions() Options { PullTimeout: 5 * time.Second, MessageLimit: 10, InitialTermination: 60 * time.Second, + RenewMargin: 10 * time.Second, BufferSize: 16, } } @@ -67,6 +72,9 @@ func (o Options) withDefaults() Options { if o.InitialTermination > 0 { d.InitialTermination = o.InitialTermination } + if o.RenewMargin > 0 { + d.RenewMargin = o.RenewMargin + } if o.BufferSize > 0 { d.BufferSize = o.BufferSize } @@ -179,6 +187,17 @@ func (s *Stream) run(ctx context.Context) { defer close(s.events) defer close(s.errors) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + s.renewLoop(ctx) + }() + s.pullLoop(ctx) + wg.Wait() +} + +func (s *Stream) pullLoop(ctx context.Context) { for { if ctx.Err() != nil { return @@ -186,9 +205,9 @@ func (s *Stream) run(ctx context.Context) { msgs, err := pullMessages(s.caller, s.pullPoint, s.opts) if err != nil { s.surfaceError(err) - // Brief backoff before retrying; reconnect-on-error - // lands in a follow-up commit and replaces this with - // proper subscription recreation. + // Brief backoff before retrying; automatic + // subscription recreation lands in the reconnect + // commit and replaces this fallback. if !sleepCtx(ctx, time.Second) { return } @@ -206,6 +225,33 @@ func (s *Stream) run(ctx context.Context) { } } +// renewLoop refreshes the subscription before InitialTermination expires. +// Exits when ctx is cancelled. +func (s *Stream) renewLoop(ctx context.Context) { + interval := s.opts.InitialTermination - s.opts.RenewMargin + if interval <= 0 { + // Pathological config (margin >= termination): fall back to + // renewing at half the termination so we still refresh, + // rather than busy-looping or never renewing. + interval = s.opts.InitialTermination / 2 + if interval <= 0 { + interval = time.Second + } + } + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if err := renewPullPoint(s.caller, s.pullPoint, s.opts); err != nil { + s.surfaceError(fmt.Errorf("renew pull point: %w", err)) + } + } + } +} + // surfaceError sends err on the errors channel non-blockingly so a // stalled consumer cannot block the pull loop. func (s *Stream) surfaceError(err error) { @@ -284,6 +330,20 @@ func pullMessages(c caller, endpoint string, opts Options) ([]event.Notification return decoded.NotificationMessage, nil } +func renewPullPoint(c caller, endpoint string, opts Options) error { + req := event.Renew{TerminationTime: xsd.String(durationToXSD(opts.InitialTermination))} + body, err := xml.Marshal(req) + if err != nil { + return fmt.Errorf("marshal Renew: %w", err) + } + resp, err := c.SendSoap(endpoint, string(body)) + if err != nil { + return err + } + _, err = readClose(resp) + return err +} + func unsubscribePullPoint(c caller, endpoint string) error { if endpoint == "" { return nil diff --git a/event/stream/topics.go b/event/stream/topics.go index 9aba5be..469027b 100644 --- a/event/stream/topics.go +++ b/event/stream/topics.go @@ -168,7 +168,7 @@ var topicRules = []struct { // tns1:RuleEngine/LineDetector/Crossed — line crossing (Hikvision, // Bosch IVA, others). Data: ObjectId (xsd:int); edge-triggered, no // State boolean. - // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.4 + // https://www.onvif.org/specs/srv/analytics/ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.4 {"LineDetector/Crossed", KindObjectDetected}, // tns1:RuleEngine/FieldDetector/ObjectsInside — intrusion / region From 82f98cb82479d0b995bdf43b16e2084bc418085d Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:40:42 +0200 Subject: [PATCH 32/53] feat(event/stream): recreate subscription after consecutive pull failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds automatic CreatePullPointSubscription recreation when the pull loop hits ReconnectAfterFailures (default 3) consecutive errors. Mirrors what production ONVIF clients (Home Assistant event_manager, Milestone integration) do because pull points die for many reasons none of which surface as a clean SOAP fault: camera reboot, NAT session timeout, subscription garbage-collected after a renew miss, firmware bug. Recreating is the only reliable recovery; Renew alone cannot save an already-dropped subscription. Two new options --------------- * ReconnectAfterFailures int (default 3) — how many consecutive pull failures trigger recreate. Conservative default; tunable for always-on cameras vs flaky NAT. * RetryBackoff time.Duration (default 1s) — base sleep between pull retries; recreate failures double this up to a 30s cap so a permanently broken camera does not hammer the network. Lifecycle changes ----------------- * Stream.pullPoint is now mutex-protected — the renew goroutine reads it concurrently with the pull loop installing a new address after recreate. getPullPoint/setPullPoint accessors keep the locking contained. * On successful recreate, failure count and backoff reset to defaults so the loop is back to its happy-path cadence. * On recreate failure, the loop continues retrying (until ctx cancel) with exponentially increasing sleep — never blocks Close. Tests cover: post-failure recreate hits a different SubscriptionRef Address and subsequent events come from the new endpoint; exponential backoff drives multiple recreate attempts when the camera stays down; defaults match production-sensible 3 failures / 1s backoff. -race clean. --- event/stream/reconnect_test.go | 120 +++++++++++++++++++++++++++++++++ event/stream/stream.go | 99 ++++++++++++++++++++++----- 2 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 event/stream/reconnect_test.go diff --git a/event/stream/reconnect_test.go b/event/stream/reconnect_test.go new file mode 100644 index 0000000..fd229a5 --- /dev/null +++ b/event/stream/reconnect_test.go @@ -0,0 +1,120 @@ +package stream + +import ( + "context" + "errors" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// createPullPointRespAlt mirrors the first fixture but returns a +// different SubscriptionReference Address so a test can prove that +// subsequent pulls hit the recreated endpoint. +const createPullPointRespAlt = ` + + + + + http://camera.local/onvif/Events/PullSub_2 + + 2026-05-21T10:30:10Z + 2026-05-21T10:31:10Z + + +` + +func TestStream_RecreatesSubscriptionAfterRepeatedPullErrors(t *testing.T) { + fc := newFakeCaller() + // Initial subscription. + fc.queueCallMethod(createPullPointResp, nil) + // Recreated subscription returns a *different* endpoint. + fc.queueCallMethod(createPullPointRespAlt, nil) + + // First pull fails. With ReconnectAfterFailures=1 this triggers a + // recreate; subsequent pulls go to PullSub_2 which we'll observe. + fc.queueSendSoap("", errors.New("transient failure")) + fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + DeviceID: "cam-1", + PullTimeout: 50 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, // keep renew quiet + }) + require.NoError(t, err) + defer s.Close() + + ev := receive(t, s.Events(), 2*time.Second) + assert.Equal(t, KindMotion, ev.Kind) + + fc.mu.Lock() + defer fc.mu.Unlock() + require.Len(t, fc.callMethodCalls, 2, + "expected exactly 2 CallMethod calls (initial + recreate)") + // The PullMessages call that delivered the motion event must + // target the new endpoint. + var newEndpointPulls int + for _, c := range fc.sendSoapCalls { + if c[0] == "http://camera.local/onvif/Events/PullSub_2" { + newEndpointPulls++ + } + } + assert.GreaterOrEqual(t, newEndpointPulls, 1, + "expected pulls against the recreated subscription endpoint") +} + +func TestStream_BackoffWhenRecreateFails(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // After the initial successful create, every CallMethod (recreate) + // and SendSoap (pull) fails. The loop should keep retrying with + // exponential backoff rather than blocking forever or spinning. + fc.mu.Lock() + fc.defaultCall = fakeResp{err: errors.New("recreate fail")} + fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(2 * time.Second) + var calls atomic.Int32 + for time.Now().Before(deadline) { + fc.mu.Lock() + calls.Store(int32(len(fc.callMethodCalls))) + fc.mu.Unlock() + if calls.Load() >= 4 { + break + } + time.Sleep(20 * time.Millisecond) + } + assert.GreaterOrEqual(t, calls.Load(), int32(4), + "expected stream to retry recreate (>=3 retries on top of the initial create)") +} + +func TestStream_ReconnectAfterFailuresDefault(t *testing.T) { + o := defaultOptions() + assert.Equal(t, 3, o.ReconnectAfterFailures) +} + +func TestStream_RetryBackoffDefault(t *testing.T) { + o := defaultOptions() + assert.Equal(t, time.Second, o.RetryBackoff) +} diff --git a/event/stream/stream.go b/event/stream/stream.go index 102f88e..a7cc06b 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -45,6 +45,17 @@ type Options struct { // renew loop fires. Larger margins tolerate slower networks at the // cost of more renew SOAP calls. Default: 10s. RenewMargin time.Duration + // ReconnectAfterFailures is the consecutive PullMessages failure + // count that triggers a CreatePullPointSubscription recreate. The + // camera or pull-point can die for many reasons (camera reboot, + // subscription garbage-collected after a renew miss, intermediate + // NAT timeout); rebuilding the subscription is the only reliable + // recovery. Default: 3. + ReconnectAfterFailures int + // RetryBackoff is the initial sleep between a pull/recreate failure + // and the next attempt. Recreate failures double this up to a 30s + // ceiling. Default: 1s. + RetryBackoff time.Duration // BufferSize is the buffer size of the Events and Errors channels. // Larger buffers absorb consumer hiccups at the cost of memory. // Default: 16. @@ -53,11 +64,13 @@ type Options struct { func defaultOptions() Options { return Options{ - PullTimeout: 5 * time.Second, - MessageLimit: 10, - InitialTermination: 60 * time.Second, - RenewMargin: 10 * time.Second, - BufferSize: 16, + PullTimeout: 5 * time.Second, + MessageLimit: 10, + InitialTermination: 60 * time.Second, + RenewMargin: 10 * time.Second, + ReconnectAfterFailures: 3, + RetryBackoff: time.Second, + BufferSize: 16, } } @@ -75,6 +88,12 @@ func (o Options) withDefaults() Options { if o.RenewMargin > 0 { d.RenewMargin = o.RenewMargin } + if o.ReconnectAfterFailures > 0 { + d.ReconnectAfterFailures = o.ReconnectAfterFailures + } + if o.RetryBackoff > 0 { + d.RetryBackoff = o.RetryBackoff + } if o.BufferSize > 0 { d.BufferSize = o.BufferSize } @@ -83,6 +102,9 @@ func (o Options) withDefaults() Options { return d } +// maxRecreateBackoff caps exponential backoff between recreate attempts. +const maxRecreateBackoff = 30 * time.Second + // caller is the subset of *onvif.Device the Stream depends on. Tests // substitute a fake; production code uses the device adapter. type caller interface { @@ -107,9 +129,11 @@ func (d deviceCaller) SendSoap(endpoint, body string) (*http.Response, error) { // A Stream is safe for concurrent use by Close from any goroutine while // readers consume Events / Errors; Close is idempotent. type Stream struct { - caller caller - opts Options - pullPoint string + caller caller + opts Options + + pullPointMu sync.Mutex + pullPoint string events chan Event errors chan error @@ -124,6 +148,18 @@ type Stream struct { now func() time.Time } +func (s *Stream) getPullPoint() string { + s.pullPointMu.Lock() + defer s.pullPointMu.Unlock() + return s.pullPoint +} + +func (s *Stream) setPullPoint(addr string) { + s.pullPointMu.Lock() + defer s.pullPointMu.Unlock() + s.pullPoint = addr +} + // NewStream creates a Stream against an ONVIF device. It performs the // CreatePullPointSubscription call synchronously so connectivity and // authentication problems surface immediately as an error rather than @@ -175,7 +211,7 @@ func (s *Stream) Close() error { // Unsubscribe is best-effort: if the camera is unreachable // the subscription will expire on its own at // InitialTermination + Renew interval anyway. - if err := unsubscribePullPoint(s.caller, s.pullPoint); err != nil { + if err := unsubscribePullPoint(s.caller, s.getPullPoint()); err != nil { s.closeErr = fmt.Errorf("unsubscribe pull point: %w", err) } }) @@ -198,21 +234,31 @@ func (s *Stream) run(ctx context.Context) { } func (s *Stream) pullLoop(ctx context.Context) { + var failures int + recreateBackoff := s.opts.RetryBackoff + for { if ctx.Err() != nil { return } - msgs, err := pullMessages(s.caller, s.pullPoint, s.opts) + msgs, err := pullMessages(s.caller, s.getPullPoint(), s.opts) if err != nil { s.surfaceError(err) - // Brief backoff before retrying; automatic - // subscription recreation lands in the reconnect - // commit and replaces this fallback. - if !sleepCtx(ctx, time.Second) { + failures++ + if failures >= s.opts.ReconnectAfterFailures { + if !s.attemptRecreate(ctx, &failures, &recreateBackoff) { + return + } + continue + } + if !sleepCtx(ctx, s.opts.RetryBackoff) { return } continue } + // Successful pull resets failure tracking. + failures = 0 + recreateBackoff = s.opts.RetryBackoff observedAt := s.now() for _, m := range msgs { ev := Decode(m, s.opts.DeviceID, observedAt) @@ -225,6 +271,29 @@ func (s *Stream) pullLoop(ctx context.Context) { } } +// attemptRecreate calls CreatePullPointSubscription and on success +// installs the new endpoint atomically. Returns false if ctx was +// cancelled while waiting for backoff (caller should exit the run +// loop). +func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) bool { + addr, err := createPullPoint(s.caller, s.opts) + if err != nil { + s.surfaceError(fmt.Errorf("recreate pull point: %w", err)) + if !sleepCtx(ctx, *backoff) { + return false + } + *backoff *= 2 + if *backoff > maxRecreateBackoff { + *backoff = maxRecreateBackoff + } + return true + } + s.setPullPoint(addr) + *failures = 0 + *backoff = s.opts.RetryBackoff + return true +} + // renewLoop refreshes the subscription before InitialTermination expires. // Exits when ctx is cancelled. func (s *Stream) renewLoop(ctx context.Context) { @@ -245,7 +314,7 @@ func (s *Stream) renewLoop(ctx context.Context) { case <-ctx.Done(): return case <-ticker.C: - if err := renewPullPoint(s.caller, s.pullPoint, s.opts); err != nil { + if err := renewPullPoint(s.caller, s.getPullPoint(), s.opts); err != nil { s.surfaceError(fmt.Errorf("renew pull point: %w", err)) } } From 6465564f2aa7f60501e266c832fc4db7b4d941dc Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:40:57 +0200 Subject: [PATCH 33/53] style(event/stream): align reconnect_test Options struct literal gofmt -w pass on reconnect_test.go. The Options struct field names had mismatched alignment; reformatted to match gofmt canonical layout. No behaviour change. --- event/stream/reconnect_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/event/stream/reconnect_test.go b/event/stream/reconnect_test.go index fd229a5..9162768 100644 --- a/event/stream/reconnect_test.go +++ b/event/stream/reconnect_test.go @@ -44,11 +44,11 @@ func TestStream_RecreatesSubscriptionAfterRepeatedPullErrors(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() s, err := newStream(ctx, fc, Options{ - DeviceID: "cam-1", - PullTimeout: 50 * time.Millisecond, - ReconnectAfterFailures: 1, - RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, // keep renew quiet + DeviceID: "cam-1", + PullTimeout: 50 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, // keep renew quiet }) require.NoError(t, err) defer s.Close() From 176e0d8f3c5b4157f7f0f1a46e4d5d3e15dbceca Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:42:02 +0200 Subject: [PATCH 34/53] feat(examples): add event/stream CLI for live camera verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a small command at examples/event/stream that opens a real ONVIF event stream against a camera and prints decoded events one per line. Intended for verifying the classifier against actual hardware (AXIS in particular) and as runnable documentation for new consumers of the package — point it at a configured camera, trigger motion, watch the events arrive. Behaviour --------- * Required flags: -xaddr, -username, -password (matches existing examples/event/* commands so anyone running the older subscribe / pullmessage demos already knows the shape). * Optional -filter passes through to Options.TopicFilter; default empty so AXIS works out of the box. * -duration N stops after N (default 0 = run until Ctrl-C). * Prints kind/state/op/topic on each event plus source and data maps when present, so multi-item ONVIF payloads (AXIS AOA active+classType+confidence, DigitalInput InputToken+LogicalState) are visible without re-reading PullMessages SOAP. * Errors channel surfaced to stderr via log; the stream auto-recovers per the reconnect logic in stream.go so transient errors do not terminate the demo. Not part of any CI; not a production tool — this is a verification harness. --- examples/event/stream/main.go | 107 ++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 examples/event/stream/main.go diff --git a/examples/event/stream/main.go b/examples/event/stream/main.go new file mode 100644 index 0000000..a795ed4 --- /dev/null +++ b/examples/event/stream/main.go @@ -0,0 +1,107 @@ +// Command streamtest opens an event stream against an ONVIF camera and +// prints decoded events as they arrive. Useful for verifying the +// classifier against real-camera topics; not intended as a production +// tool. +// +// Example: +// +// go run ./examples/event/stream \ +// -xaddr 192.168.1.10 \ +// -username root -password admin \ +// -duration 60s +// +// The xaddr is the camera's host or host:port (the library appends +// /onvif/device_service); pass with no protocol prefix. +package main + +import ( + "context" + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + "time" + + "github.com/kerberos-io/onvif" + "github.com/kerberos-io/onvif/event/stream" +) + +func main() { + xaddr := flag.String("xaddr", "", "camera host or host:port (required)") + username := flag.String("username", "", "ONVIF user (required)") + password := flag.String("password", "", "ONVIF password (required)") + deviceID := flag.String("device-id", "", "logical name printed with each event (default: xaddr)") + filter := flag.String("filter", "", "raw ONVIF ConcreteSet topic filter (empty = all topics, works on AXIS)") + pullTimeout := flag.Duration("pull-timeout", 5*time.Second, "server-side wait per PullMessages call") + duration := flag.Duration("duration", 0, "stop after this long (0 = run until Ctrl-C)") + flag.Parse() + + if *xaddr == "" || *username == "" || *password == "" { + flag.Usage() + os.Exit(2) + } + if *deviceID == "" { + *deviceID = *xaddr + } + + dev, err := onvif.NewDevice(onvif.DeviceParams{ + Xaddr: *xaddr, + Username: *username, + Password: *password, + AuthMode: onvif.UsernameTokenAuth, + }) + if err != nil { + log.Fatalf("connect: %v", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + if *duration > 0 { + var done context.CancelFunc + ctx, done = context.WithTimeout(ctx, *duration) + defer done() + } + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-sigs + cancel() + }() + + s, err := stream.NewStream(ctx, dev, stream.Options{ + DeviceID: *deviceID, + TopicFilter: *filter, + PullTimeout: *pullTimeout, + }) + if err != nil { + log.Fatalf("open stream: %v", err) + } + defer s.Close() + + log.Printf("streaming from %s (device-id=%s, filter=%q)", *xaddr, *deviceID, *filter) + for { + select { + case <-ctx.Done(): + log.Printf("done (%v)", ctx.Err()) + return + case ev, ok := <-s.Events(): + if !ok { + return + } + fmt.Printf("%s kind=%-15s state=%-9s op=%-12s topic=%s", + ev.Timestamp.Format(time.RFC3339), ev.Kind, ev.State, ev.Operation, ev.Topic) + if len(ev.Source) > 0 { + fmt.Printf(" source=%v", ev.Source) + } + if len(ev.Data) > 0 { + fmt.Printf(" data=%v", ev.Data) + } + fmt.Println() + case e := <-s.Errors(): + log.Printf("stream error: %v", e) + } + } +} From 93620f04a357fbeaee7ec302872b3841f4a9b5a0 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:54:05 +0200 Subject: [PATCH 35/53] fix(event/stream): production-grade SOAP and lifecycle hardening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the five ship-blocker findings from the second review: 1. Bounded body read (review F1 / R-HIGH) readClose now wraps resp.Body with io.LimitReader(10 MiB). A hostile or buggy camera streaming an unbounded body cannot OOM the agent. Legitimate PullMessages payloads are <200KB even with dense analytics. 2. SOAP Fault detection (review F3) unmarshalNode now scans for SOAP 1.1 faultstring and SOAP 1.2 Reason/Text BEFORE the missing-element error path. Auth failures ('not authorized'), InvalidFilterFault and expired-subscription faults now surface their reason text instead of collapsing to the unhelpful 'response missing PullMessagesResponse element'. This is the difference between a debuggable error and a hidden one when a customer's credentials change. 3. Absolute Renew TerminationTime (review F1 wire-correctness) renewPullPoint now sends an RFC3339 UTC datetime ('2026-05-21T10:30:00Z') instead of a relative xsd:duration ('PT60S'). WS-BaseNotification §6.1.1 accepts both, but older Hikvision, some Dahua and Bosch firmwares only accept the absolute form — the library's own type comment even flags this ('BUG(r) Bad AbsoluteOrRelativeTimeType type'). 4. Bounded Close (review P0) Close now wraps Unsubscribe in a 5s timeout. Previously a TCP-accepted-but-never-replying camera would wedge Close indefinitely; now Close returns with a timeout error and the subscription expires on its own at InitialTermination. 5. Explicit channel-close ordering after wg.Wait The run goroutine previously relied on defer-LIFO to guarantee renew exits before close(errors). Future maintainers extending run() could invert that order silently. Closes are now explicit sequential statements after wg.Wait() so the invariant is local, not order-of-defers magic. Also expands wsnt:UtcTime parsing in decode.go to cover the four formats observed across vendor firmwares: RFC3339 with sub-seconds, compact offsets ('+0200', Geovision/Dahua), and naked timestamps without timezone (older Hikvision; per spec UTC is implied). Caller interface gains a doc comment noting it must be safe for concurrent use, documenting the contract Stream depends on (*onvif. Device satisfies it via http.Client). Tests added: SOAP 1.1 and 1.2 fault extraction, fault surfacing through unmarshalNode, Renew absolute-datetime assertion, Close-with-blocked-Unsubscribe returning within the timeout. -race clean. --- event/stream/decode.go | 22 ++++- event/stream/soap_test.go | 155 ++++++++++++++++++++++++++++++++++++ event/stream/stream.go | 94 +++++++++++++++++++--- event/stream/stream_test.go | 27 +++++-- 4 files changed, 275 insertions(+), 23 deletions(-) create mode 100644 event/stream/soap_test.go diff --git a/event/stream/decode.go b/event/stream/decode.go index 1d96941..f0bc7df 100644 --- a/event/stream/decode.go +++ b/event/stream/decode.go @@ -88,17 +88,31 @@ func parsePropertyOperation(s string) PropertyOperation { // time when the attribute is absent or unparseable. The result is // normalised to UTC so equality comparisons across timezones work. // -// xsd:dateTime in ONVIF messages is RFC 3339 in practice; we try -// time.RFC3339Nano first (covers sub-second precision) and fall back to -// time.RFC3339 for cameras that drop the fractional part. +// xsd:dateTime in ONVIF messages is RFC 3339 in practice but real +// cameras emit several flavours: with/without sub-seconds, with colon +// or compact ("+0200") timezone offsets, and some older Hikvision +// firmwares omit the timezone entirely (treated as UTC per +// WS-BaseNotification which mandates UTC for UtcTime). func parseDeviceTime(s string) time.Time { if s == "" { return time.Time{} } - for _, layout := range []string{time.RFC3339Nano, time.RFC3339} { + for _, layout := range deviceTimeLayouts { if t, err := time.Parse(layout, s); err == nil { return t.UTC() } } return time.Time{} } + +// deviceTimeLayouts lists the wsnt:UtcTime forms observed across vendor +// firmwares. Ordered from most-precise / most-common first so the +// happy path hits early. +var deviceTimeLayouts = []string{ + time.RFC3339Nano, // 2026-05-21T10:30:00.500Z, ...+02:00 + time.RFC3339, // 2026-05-21T10:30:00Z, ...+02:00 + "2006-01-02T15:04:05.999-0700", // sub-second + compact offset (Geovision) + "2006-01-02T15:04:05-0700", // compact offset (some Dahua) + "2006-01-02T15:04:05.999", // no timezone, sub-second (rare) + "2006-01-02T15:04:05", // naked, no TZ (older Hikvision) +} diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go new file mode 100644 index 0000000..e8c48f5 --- /dev/null +++ b/event/stream/soap_test.go @@ -0,0 +1,155 @@ +package stream + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --- SOAP fault detection --------------------------------------------- + +func TestExtractSOAPFault_SOAP11(t *testing.T) { + body := ` + + + + env:Client + The action requested requires authorization and the sender is not authorized + + +` + got := extractSOAPFault(body) + assert.Contains(t, got, "not authorized") +} + +func TestExtractSOAPFault_SOAP12(t *testing.T) { + body := ` + + + + env:Sender + Subscription has expired + + +` + got := extractSOAPFault(body) + assert.Contains(t, got, "Subscription has expired") +} + +func TestExtractSOAPFault_NotAFault(t *testing.T) { + assert.Empty(t, extractSOAPFault(createPullPointResp)) +} + +func TestExtractSOAPFault_EmptyBody(t *testing.T) { + assert.Empty(t, extractSOAPFault("")) +} + +func TestUnmarshalNode_ReturnsFaultReasonInsteadOfMissingElement(t *testing.T) { + body := ` + not authorized +` + var out struct{} + err := unmarshalNode(body, "PullMessagesResponse", &out) + require.Error(t, err) + assert.Contains(t, err.Error(), "not authorized") + assert.NotContains(t, err.Error(), "missing PullMessagesResponse") +} + +// --- Renew sends absolute datetime ----------------------------------- + +func TestRenew_SendsAbsoluteDateTimeNotDuration(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + InitialTermination: 30 * time.Millisecond, + RenewMargin: 5 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(500 * time.Millisecond) + for time.Now().Before(deadline) { + if countSendSoapMatching(fc, "Renew") >= 1 { + break + } + time.Sleep(10 * time.Millisecond) + } + + fc.mu.Lock() + defer fc.mu.Unlock() + var renewBody string + for _, c := range fc.sendSoapCalls { + if strings.Contains(c[1], "Renew") { + renewBody = c[1] + break + } + } + require.NotEmpty(t, renewBody, "no Renew call observed") + // Absolute form is "YYYY-MM-DDTHH:MM:SSZ" not "PTnS". + assert.NotContains(t, renewBody, "PT", "Renew should not send relative duration; some firmwares reject it") + assert.Regexp(t, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`, renewBody, "Renew should send absolute RFC3339 UTC") +} + +// --- Bounded body read ----------------------------------------------- + +func TestReadClose_LimitsBodySize(t *testing.T) { + // Build a response with a body just over the limit. readClose must + // not return more than the limit even if the camera pretends to + // send more. + if maxResponseBytes < 1024 { + t.Skip("limit too small for this test") + } + big := strings.Repeat("A", maxResponseBytes+1024) + // Wrap in a minimal SOAP envelope so the body is at least + // well-formed shape-wise. + body := "" + big + "" + fc := newFakeCaller() + fc.queueCallMethod(body, nil) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Construction will fail because the truncated body has no + // CreatePullPointSubscriptionResponse — that's fine; what matters + // is the read completes without OOM. + _, err := newStream(ctx, fc, Options{}) + assert.Error(t, err) +} + +// --- Close timeout --------------------------------------------------- + +func TestClose_BoundedByTimeoutOnHungUnsubscribe(t *testing.T) { + // Patch closeUnsubscribeTimeout for the duration of the test so the + // assertion completes promptly. We can't change the const at runtime + // so we use a short InitialTermination and verify Close still + // returns within closeUnsubscribeTimeout + slack rather than + // blocking forever. + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + block := make(chan struct{}) + defer close(block) // release the hung Unsubscribe so the fake's goroutine exits + fc.mu.Lock() + fc.blockUnsubscribe = block + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) + require.NoError(t, err) + + start := time.Now() + err = s.Close() + elapsed := time.Since(start) + // Unsubscribe is hung, so Close must surface a timeout error from + // the bounded wait rather than block forever. closeUnsubscribeTimeout + // is 5s; allow 1s slack for scheduling. + require.Error(t, err) + assert.Contains(t, err.Error(), "timeout") + assert.Less(t, elapsed, closeUnsubscribeTimeout+time.Second, + "Close exceeded bound (%s); expected ~%s", elapsed, closeUnsubscribeTimeout) +} diff --git a/event/stream/stream.go b/event/stream/stream.go index a7cc06b..9e4fd7a 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -8,7 +8,9 @@ import ( "fmt" "io" "net/http" + "regexp" "strconv" + "strings" "sync" "time" @@ -17,6 +19,18 @@ import ( "github.com/kerberos-io/onvif/xsd" ) +// maxResponseBytes caps the size of a SOAP response we will buffer in +// memory. ONVIF PullMessages bodies are normally <100KB even with dense +// analytics payloads; 10 MiB is comfortably above legitimate traffic +// while keeping a hostile or buggy camera from OOMing the process. +const maxResponseBytes = 10 << 20 + +// closeUnsubscribeTimeout bounds the SOAP Unsubscribe call issued by +// Close so a hung camera connection cannot wedge the caller. The +// subscription expires at the camera anyway once InitialTermination +// elapses, so a missed unsubscribe is at worst cosmetic. +const closeUnsubscribeTimeout = 5 * time.Second + // Options configures a Stream. The zero value is usable; defaultOptions // fills in production-sensible defaults for any unset field. type Options struct { @@ -107,6 +121,11 @@ const maxRecreateBackoff = 30 * time.Second // caller is the subset of *onvif.Device the Stream depends on. Tests // substitute a fake; production code uses the device adapter. +// +// Implementations must be safe for concurrent use: the pull loop and +// renew loop call into caller from separate goroutines. *onvif.Device +// satisfies this because its HTTP client is the goroutine-safe +// http.Client. type caller interface { CallMethod(method any) (*http.Response, error) SendSoap(endpoint, body string) (*http.Response, error) @@ -204,25 +223,33 @@ func (s *Stream) Errors() <-chan error { return s.errors } // Close stops the background goroutine, waits for it to exit, and // unsubscribes from the camera. Subsequent calls are no-ops. +// +// Unsubscribe is bounded by closeUnsubscribeTimeout so a hung camera +// connection cannot wedge the caller. On timeout Close still returns +// promptly; the subscription will expire at the camera once +// InitialTermination + RenewMargin elapses without a renew. func (s *Stream) Close() error { s.closeOnce.Do(func() { s.cancel() <-s.done - // Unsubscribe is best-effort: if the camera is unreachable - // the subscription will expire on its own at - // InitialTermination + Renew interval anyway. - if err := unsubscribePullPoint(s.caller, s.getPullPoint()); err != nil { - s.closeErr = fmt.Errorf("unsubscribe pull point: %w", err) + + errCh := make(chan error, 1) + go func() { + errCh <- unsubscribePullPoint(s.caller, s.getPullPoint()) + }() + select { + case err := <-errCh: + if err != nil { + s.closeErr = fmt.Errorf("unsubscribe pull point: %w", err) + } + case <-time.After(closeUnsubscribeTimeout): + s.closeErr = fmt.Errorf("unsubscribe pull point: timeout after %s", closeUnsubscribeTimeout) } }) return s.closeErr } func (s *Stream) run(ctx context.Context) { - defer close(s.done) - defer close(s.events) - defer close(s.errors) - var wg sync.WaitGroup wg.Add(1) go func() { @@ -231,6 +258,13 @@ func (s *Stream) run(ctx context.Context) { }() s.pullLoop(ctx) wg.Wait() + + // Explicit close order after both goroutines have exited so a + // future maintainer extending this function does not accidentally + // rely on defer-ordering for channel-close safety. + close(s.errors) + close(s.events) + close(s.done) } func (s *Stream) pullLoop(ctx context.Context) { @@ -400,7 +434,12 @@ func pullMessages(c caller, endpoint string, opts Options) ([]event.Notification } func renewPullPoint(c caller, endpoint string, opts Options) error { - req := event.Renew{TerminationTime: xsd.String(durationToXSD(opts.InitialTermination))} + // WS-BaseNotification §6.1.1 declares TerminationTime as + // xsd:dateTime OR xsd:duration, but older Hikvision, some Dahua + // and some Bosch firmwares reject the relative-duration form. Send + // an absolute UTC datetime to match what production NVRs do. + absoluteEnd := time.Now().UTC().Add(opts.InitialTermination).Format("2006-01-02T15:04:05Z") + req := event.Renew{TerminationTime: xsd.String(absoluteEnd)} body, err := xml.Marshal(req) if err != nil { return fmt.Errorf("marshal Renew: %w", err) @@ -434,7 +473,9 @@ func readClose(resp *http.Response) (string, error) { return "", errors.New("nil HTTP response") } defer resp.Body.Close() - b, err := io.ReadAll(resp.Body) + // LimitReader prevents a hostile or buggy camera from OOMing the + // agent by streaming an unbounded response body. + b, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) if err != nil { return "", fmt.Errorf("read response body: %w", err) } @@ -445,7 +486,15 @@ func readClose(resp *http.Response) (string, error) { // name and decodes it into out. ONVIF SOAP responses come wrapped in an // envelope with multiple namespace prefixes; this helper sidesteps // namespace matching by keying on local name only. +// +// When the camera returns a SOAP Fault instead of the expected +// response, the fault reason is surfaced as the error so callers can +// distinguish "auth failed" / "subscription expired" from "unparseable +// response". func unmarshalNode(body, localName string, out any) error { + if reason := extractSOAPFault(body); reason != "" { + return fmt.Errorf("ONVIF SOAP fault: %s", reason) + } dec := xml.NewDecoder(bytes.NewBufferString(body)) for { tok, err := dec.Token() @@ -469,6 +518,29 @@ func unmarshalNode(body, localName string, out any) error { } } +var ( + // SOAP 1.1: reason + soap11FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?faultstring[^>]*>(.*?)\s]+:)?faultstring>`) + // SOAP 1.2: ...reason... + soap12FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Reason\b[^>]*>.*?<(?:[^:>\s]+:)?Text[^>]*>(.*?)\s]+:)?Text>`) +) + +// extractSOAPFault returns the human-readable reason text from a SOAP +// fault, or empty string when the body is not a fault. Handles both +// SOAP 1.1 (faultstring) and SOAP 1.2 (Reason/Text) shapes. +func extractSOAPFault(body string) string { + if !strings.Contains(body, "Fault") { + return "" + } + if m := soap11FaultRE.FindStringSubmatch(body); len(m) > 1 { + return strings.TrimSpace(m[1]) + } + if m := soap12FaultRE.FindStringSubmatch(body); len(m) > 1 { + return strings.TrimSpace(m[1]) + } + return "" +} + // durationToXSD formats a Go time.Duration as an xsd:duration string in // PTnS form. Second precision is sufficient — ONVIF cameras do not // honour sub-second pull timeouts and intermediate routers may round in diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index 698d07e..010f10a 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -20,14 +20,19 @@ import ( // returns the next queued response; when the queue is exhausted it falls // back to a default response so the indefinite pull loop does not // require tests to enumerate every call. +// +// blockUnsubscribe, when non-nil, causes SendSoap calls whose body +// contains "Unsubscribe" to block until the channel is closed. Used to +// verify Close's timeout path. type fakeCaller struct { - mu sync.Mutex - callMethodResps []fakeResp - sendSoapResps []fakeResp - defaultSendSoap fakeResp - defaultCall fakeResp - callMethodCalls []any - sendSoapCalls [][2]string + mu sync.Mutex + callMethodResps []fakeResp + sendSoapResps []fakeResp + defaultSendSoap fakeResp + defaultCall fakeResp + callMethodCalls []any + sendSoapCalls [][2]string + blockUnsubscribe chan struct{} } type fakeResp struct { @@ -72,13 +77,19 @@ func (f *fakeCaller) CallMethod(m any) (*http.Response, error) { func (f *fakeCaller) SendSoap(endpoint, body string) (*http.Response, error) { f.mu.Lock() - defer f.mu.Unlock() f.sendSoapCalls = append(f.sendSoapCalls, [2]string{endpoint, body}) r := f.defaultSendSoap if len(f.sendSoapResps) > 0 { r = f.sendSoapResps[0] f.sendSoapResps = f.sendSoapResps[1:] } + block := f.blockUnsubscribe + f.mu.Unlock() + + if block != nil && strings.Contains(body, "Unsubscribe") { + <-block + } + if r.err != nil { return nil, r.err } From d718145bd3d956e4ed5da55baab2357c9a622755 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:54:25 +0200 Subject: [PATCH 36/53] style(event/stream): gofmt decode.go layout table alignment --- event/stream/decode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/event/stream/decode.go b/event/stream/decode.go index f0bc7df..2559a8b 100644 --- a/event/stream/decode.go +++ b/event/stream/decode.go @@ -109,8 +109,8 @@ func parseDeviceTime(s string) time.Time { // firmwares. Ordered from most-precise / most-common first so the // happy path hits early. var deviceTimeLayouts = []string{ - time.RFC3339Nano, // 2026-05-21T10:30:00.500Z, ...+02:00 - time.RFC3339, // 2026-05-21T10:30:00Z, ...+02:00 + time.RFC3339Nano, // 2026-05-21T10:30:00.500Z, ...+02:00 + time.RFC3339, // 2026-05-21T10:30:00Z, ...+02:00 "2006-01-02T15:04:05.999-0700", // sub-second + compact offset (Geovision) "2006-01-02T15:04:05-0700", // compact offset (some Dahua) "2006-01-02T15:04:05.999", // no timezone, sub-second (rare) From 6fc9b23e9fa4b87c110eee3c037590aa31505532 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:55:58 +0200 Subject: [PATCH 37/53] feat(event/stream): typed errors and AfterReconnect observability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the bare fmt.Errorf wrappers on the Errors channel with three typed errors and adds an Event.AfterReconnect flag so consumers can distinguish post-recreate replay events from live ones. Typed errors ------------ ErrPullFailed, ErrRenewFailed, ErrRecreateFailed all implement Unwrap() and Op() Op. Consumers can branch with errors.As without parsing strings: var pull ErrPullFailed if errors.As(e, &pull) { /* transient; logged */ } var recreate ErrRecreateFailed if errors.As(e, &recreate) { /* alert: camera may be offline */ } Op() returns OpPull / OpRenew / OpRecreate for cases where the caller wants to log the operation name without unwrapping. Both addressed the review's 'highest-leverage v1 change' concern about bare error on the Errors channel. AfterReconnect observability ---------------------------- ONVIF cameras replay each property's current value with PropertyInitialized whenever a new pull-point subscription is established (per the Event Service spec). A consumer doing edge detection on motion = StateActive would otherwise see a phantom 'motion started' for every active property after every reconnect. The pull loop now tracks an afterReconnect flag local to the goroutine: set to true when attemptRecreate returns justRecreated, applied to every emitted event, cleared on the first non-Initialized event we see. This bounds the replay window naturally — once the camera has finished sending current state, the next event tells us we're live. attemptRecreate now returns (justRecreated, cont) so the pull loop knows whether the just-completed recreate succeeded vs. the call returning due to ctx-cancel during backoff. Test coverage ------------- * errors_test.go: typed-error Unwrap/Op assertions plus Stream-level proof that pull and recreate failures arrive on the Errors channel wearing the right type. * AfterReconnect flag: drives the stream through a failure, observes the next event carries the flag and the one after does not. --- event/stream/errors.go | 42 ++++++++++++ event/stream/errors_test.go | 133 ++++++++++++++++++++++++++++++++++++ event/stream/stream.go | 39 ++++++++--- event/stream/types.go | 9 +++ 4 files changed, 212 insertions(+), 11 deletions(-) create mode 100644 event/stream/errors.go create mode 100644 event/stream/errors_test.go diff --git a/event/stream/errors.go b/event/stream/errors.go new file mode 100644 index 0000000..bd80674 --- /dev/null +++ b/event/stream/errors.go @@ -0,0 +1,42 @@ +package stream + +import "fmt" + +// Op identifies which Stream operation failed. Used by ErrPullFailed, +// ErrRenewFailed and ErrRecreateFailed so consumers can branch with +// errors.As without parsing the wrapped message. +type Op string + +const ( + OpPull Op = "pull" + OpRenew Op = "renew" + OpRecreate Op = "recreate" +) + +// ErrPullFailed wraps a transient PullMessages failure. The pull loop +// surfaces it on the Errors channel and continues. Consumers can match +// with errors.As(err, &stream.ErrPullFailed{}) — see +// TestErrors_TypedAssertion. +type ErrPullFailed struct{ Err error } + +func (e ErrPullFailed) Error() string { return fmt.Sprintf("pull messages: %v", e.Err) } +func (e ErrPullFailed) Unwrap() error { return e.Err } +func (ErrPullFailed) Op() Op { return OpPull } + +// ErrRenewFailed wraps a Renew SOAP failure. Renew errors are usually +// recovered implicitly: the subscription dies, pull starts failing, and +// the reconnect logic recreates it. +type ErrRenewFailed struct{ Err error } + +func (e ErrRenewFailed) Error() string { return fmt.Sprintf("renew pull point: %v", e.Err) } +func (e ErrRenewFailed) Unwrap() error { return e.Err } +func (ErrRenewFailed) Op() Op { return OpRenew } + +// ErrRecreateFailed wraps a failed CreatePullPointSubscription during +// the reconnect path. The loop continues with exponential backoff; +// consumers seeing this repeatedly should consider the camera offline. +type ErrRecreateFailed struct{ Err error } + +func (e ErrRecreateFailed) Error() string { return fmt.Sprintf("recreate pull point: %v", e.Err) } +func (e ErrRecreateFailed) Unwrap() error { return e.Err } +func (ErrRecreateFailed) Op() Op { return OpRecreate } diff --git a/event/stream/errors_test.go b/event/stream/errors_test.go new file mode 100644 index 0000000..d68e347 --- /dev/null +++ b/event/stream/errors_test.go @@ -0,0 +1,133 @@ +package stream + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTypedErrors_UnwrapAndOp(t *testing.T) { + inner := errors.New("boom") + tests := []struct { + name string + err error + op Op + }{ + {"pull", ErrPullFailed{Err: inner}, OpPull}, + {"renew", ErrRenewFailed{Err: inner}, OpRenew}, + {"recreate", ErrRecreateFailed{Err: inner}, OpRecreate}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.True(t, errors.Is(tc.err, inner), "errors.Is should unwrap to inner") + assert.Contains(t, tc.err.Error(), "boom") + + // Each typed error exposes Op() for branch-without-string-parse. + if e, ok := tc.err.(interface{ Op() Op }); ok { + assert.Equal(t, tc.op, e.Op()) + } else { + t.Fatalf("%T does not expose Op()", tc.err) + } + }) + } +} + +func TestStream_PullErrorIsTypedErrPullFailed(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueSendSoap("", errors.New("transient")) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 50 * time.Millisecond, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + select { + case e := <-s.Errors(): + var pullErr ErrPullFailed + require.True(t, errors.As(e, &pullErr), "expected ErrPullFailed, got %T: %v", e, e) + assert.Contains(t, pullErr.Err.Error(), "transient") + case <-time.After(time.Second): + t.Fatal("expected ErrPullFailed on Errors channel") + } +} + +func TestStream_RecreateErrorIsTypedErrRecreateFailed(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.mu.Lock() + fc.defaultCall = fakeResp{err: errors.New("recreate fail")} + fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(time.Second) + var sawRecreate bool + for time.Now().Before(deadline) && !sawRecreate { + select { + case e := <-s.Errors(): + var rec ErrRecreateFailed + if errors.As(e, &rec) { + sawRecreate = true + assert.Contains(t, rec.Err.Error(), "recreate fail") + } + case <-time.After(50 * time.Millisecond): + } + } + assert.True(t, sawRecreate, "expected at least one ErrRecreateFailed on Errors") +} + +func TestStream_AfterReconnectFlagSetOnFirstPostRecreateBatch(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // Second create is the recreate. + fc.queueCallMethod(createPullPointRespAlt, nil) + + // First pull fails -> triggers recreate with ReconnectAfterFailures=1. + fc.queueSendSoap("", errors.New("transient")) + // First pull after recreate: a Changed motion event. The flag + // should be true, and should clear (because we received a + // non-Initialized event). + fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) + // Second pull after recreate: another motion event. Flag should + // now be false. + fc.queueSendSoap(pullMessagesResp(motionMsg("false")), nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 50 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + ev1 := receive(t, s.Events(), 2*time.Second) + assert.True(t, ev1.AfterReconnect, "first event after recreate must carry AfterReconnect=true") + assert.Equal(t, StateActive, ev1.State) + + ev2 := receive(t, s.Events(), 2*time.Second) + assert.False(t, ev2.AfterReconnect, "subsequent events should not carry AfterReconnect") + assert.Equal(t, StateInactive, ev2.State) +} diff --git a/event/stream/stream.go b/event/stream/stream.go index 9e4fd7a..f10c9c5 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -270,6 +270,7 @@ func (s *Stream) run(ctx context.Context) { func (s *Stream) pullLoop(ctx context.Context) { var failures int recreateBackoff := s.opts.RetryBackoff + var afterReconnect bool for { if ctx.Err() != nil { @@ -277,12 +278,16 @@ func (s *Stream) pullLoop(ctx context.Context) { } msgs, err := pullMessages(s.caller, s.getPullPoint(), s.opts) if err != nil { - s.surfaceError(err) + s.surfaceError(ErrPullFailed{Err: err}) failures++ if failures >= s.opts.ReconnectAfterFailures { - if !s.attemptRecreate(ctx, &failures, &recreateBackoff) { + justRecreated, cont := s.attemptRecreate(ctx, &failures, &recreateBackoff) + if !cont { return } + if justRecreated { + afterReconnect = true + } continue } if !sleepCtx(ctx, s.opts.RetryBackoff) { @@ -296,6 +301,17 @@ func (s *Stream) pullLoop(ctx context.Context) { observedAt := s.now() for _, m := range msgs { ev := Decode(m, s.opts.DeviceID, observedAt) + if afterReconnect { + ev.AfterReconnect = true + // ONVIF replays current state with + // PropertyInitialized on a new subscription. + // Clear the flag as soon as we see anything + // other than Initialized — at that point we + // have transitioned to live events. + if ev.Operation != PropertyInitialized { + afterReconnect = false + } + } select { case <-ctx.Done(): return @@ -306,26 +322,27 @@ func (s *Stream) pullLoop(ctx context.Context) { } // attemptRecreate calls CreatePullPointSubscription and on success -// installs the new endpoint atomically. Returns false if ctx was -// cancelled while waiting for backoff (caller should exit the run -// loop). -func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) bool { +// installs the new endpoint atomically. The first return is true when +// recreate succeeded just now (caller flags the next batch with +// AfterReconnect). The second return is false only if ctx was cancelled +// during backoff (caller should exit the run loop). +func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) (justRecreated, cont bool) { addr, err := createPullPoint(s.caller, s.opts) if err != nil { - s.surfaceError(fmt.Errorf("recreate pull point: %w", err)) + s.surfaceError(ErrRecreateFailed{Err: err}) if !sleepCtx(ctx, *backoff) { - return false + return false, false } *backoff *= 2 if *backoff > maxRecreateBackoff { *backoff = maxRecreateBackoff } - return true + return false, true } s.setPullPoint(addr) *failures = 0 *backoff = s.opts.RetryBackoff - return true + return true, true } // renewLoop refreshes the subscription before InitialTermination expires. @@ -349,7 +366,7 @@ func (s *Stream) renewLoop(ctx context.Context) { return case <-ticker.C: if err := renewPullPoint(s.caller, s.getPullPoint(), s.opts); err != nil { - s.surfaceError(fmt.Errorf("renew pull point: %w", err)) + s.surfaceError(ErrRenewFailed{Err: err}) } } } diff --git a/event/stream/types.go b/event/stream/types.go index fcc3612..9f1557d 100644 --- a/event/stream/types.go +++ b/event/stream/types.go @@ -159,4 +159,13 @@ type Event struct { // Timestamp for ordering and DeviceTime only for forensics or // cross-camera correlation when caller manages NTP. DeviceTime time.Time + // AfterReconnect is true for events delivered after the Stream + // silently recreated its pull-point subscription. ONVIF cameras + // replay each property's current value with PropertyInitialized on + // a new subscription, which would otherwise look like a flood of + // new state changes to a consumer doing edge-detection. Watch this + // flag to suppress duplicate handling, or treat it as a normal + // event if you only care about steady-state level. Cleared on the + // first event whose Operation is not PropertyInitialized. + AfterReconnect bool } From fd71109514008464210c2f585c53169119d38cb2 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:58:34 +0200 Subject: [PATCH 38/53] refactor(event/stream): tighten public surface per v1 review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API-shape changes flagged as 'hard to reverse after v1' by the architect reviewer. Acceptable to do now while no external code imports the package; would be breaking later. Surface tightening ------------------ * Decode unexported to decode. The Stream is the only intended caller; exposing the helper invited future API drift. Same-package tests still reach it. * TopicFilter renamed to RawTopicFilter to signal that the value is fed verbatim into the SOAP envelope and is the 'advanced escape hatch', not the supported routing surface. Callers should normally leave it empty and rely on Classify. Options zero-value policy clarified ----------------------------------- * Field godoc on every numeric option now explicitly states 'zero means default' so the policy is local, not buried in withDefaults(). * New DisableReconnect bool — addresses the ReconnectAfterFailures=0-as-disable footgun the API reviewer flagged. Reader can no longer confuse 'unset, fallback to default' with 'opt out of reconnect'. * BufferSize semantics extended: zero -> default (16), negative -> unbuffered (0), positive -> explicit size. Lets callers ask for back-pressure-only channels. Default tuning -------------- * MessageLimit default raised from 10 to 32. Busy AXIS cameras with several configured inputs / analytics rules can burst beyond 10 per pull; the lower cap meant up to one PullTimeout of added latency for the queued overflow without saving anything meaningful. 32 covers observed bursts with no real overhead on quiet pulls. Caller interface ---------------- * Doc comment now states the goroutine-safety contract Stream depends on (pull loop and renew loop call from separate goroutines). *onvif.Device satisfies it via http.Client. Package documentation --------------------- * doc.go rewritten as a real godoc landing page: usage snippet, invariants (channel close, Close idempotency, NewStream does I/O, buffer semantics), reconnect behaviour and AfterReconnect, and a pointer to topics.go for the classifier table. Replaces the earlier stub that referenced unimplemented identifiers. --- event/stream/decode.go | 9 +++-- event/stream/decode_test.go | 22 +++++------ event/stream/doc.go | 66 ++++++++++++++++++++++++++----- event/stream/stream.go | 73 ++++++++++++++++++++++------------- event/stream/stream_test.go | 2 +- examples/event/stream/main.go | 2 +- 6 files changed, 122 insertions(+), 52 deletions(-) diff --git a/event/stream/decode.go b/event/stream/decode.go index 2559a8b..ac3b88e 100644 --- a/event/stream/decode.go +++ b/event/stream/decode.go @@ -7,8 +7,11 @@ import ( "github.com/kerberos-io/onvif/event" ) -// Decode converts a single ONVIF NotificationMessage into the package's -// normalized Event representation. +// decode converts a single ONVIF NotificationMessage into the package's +// normalized Event representation. Unexported because the only intended +// caller is the Stream; downstream consumers receive decoded Events on +// the Events channel. Tests reach decode directly because they're in +// the same package. // // deviceID is supplied by the caller because the message itself does not // identify the originating camera. observedAt is recorded verbatim as @@ -18,7 +21,7 @@ import ( // When the Topic does not match any classifier rule the returned Event // has Kind == KindUnknown but Source, Data and Topic are still populated // so consumers can fall back to inspecting the wire form. -func Decode(msg event.NotificationMessage, deviceID string, observedAt time.Time) Event { +func decode(msg event.NotificationMessage, deviceID string, observedAt time.Time) Event { topic := string(msg.Topic.TopicKinds) desc := msg.Message.Message return Event{ diff --git a/event/stream/decode_test.go b/event/stream/decode_test.go index edce4c8..2d52800 100644 --- a/event/stream/decode_test.go +++ b/event/stream/decode_test.go @@ -52,7 +52,7 @@ func TestDecode_MotionActive(t *testing.T) { map[string]string{"IsMotion": "true"}, ) - ev := Decode(in, "axis-cam-01", observedAt) + ev := decode(in, "axis-cam-01", observedAt) assert.Equal(t, KindMotion, ev.Kind) assert.Equal(t, StateActive, ev.State) @@ -74,7 +74,7 @@ func TestDecode_MotionInactive(t *testing.T) { nil, map[string]string{"State": "false"}, ) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, KindMotion, ev.Kind) assert.Equal(t, StateInactive, ev.State) } @@ -88,7 +88,7 @@ func TestDecode_HanwhaNumericMotionValue(t *testing.T) { nil, map[string]string{"Motion": "1"}, ) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, KindMotion, ev.Kind) assert.Equal(t, StateActive, ev.State) } @@ -103,7 +103,7 @@ func TestDecode_AvigilonActiveLiteral(t *testing.T) { map[string]string{"RelayToken": "Relay-1"}, map[string]string{"LogicalState": "active"}, ) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, KindDigitalOutput, ev.Kind) assert.Equal(t, StateActive, ev.State) assert.Equal(t, "Relay-1", ev.Source["RelayToken"]) @@ -124,7 +124,7 @@ func TestDecode_AxisObjectAnalyticsMultiItem(t *testing.T) { "confidence": "92", }, ) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, KindObjectDetected, ev.Kind) assert.Equal(t, StateActive, ev.State) assert.Equal(t, "Human", ev.Data["classType"]) @@ -142,7 +142,7 @@ func TestDecode_LineDetectorCrossedHasNoState(t *testing.T) { map[string]string{"VideoSourceConfigurationToken": "vsct0", "Rule": "LineRule"}, map[string]string{"ObjectId": "42"}, ) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, KindObjectDetected, ev.Kind) assert.Equal(t, StateUnknown, ev.State) assert.Equal(t, "42", ev.Data["ObjectId"]) @@ -158,7 +158,7 @@ func TestDecode_UnknownTopicStillPreservesWireData(t *testing.T) { nil, map[string]string{"Custom": "true"}, ) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, KindUnknown, ev.Kind) assert.Equal(t, "tns1:UserAlarm/IVA", ev.Topic) assert.Equal(t, "true", ev.Data["Custom"]) @@ -179,7 +179,7 @@ func TestDecode_PropertyOperationVariants(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { in := msg("tns1:VideoSource/MotionAlarm", tc.in, "", nil, nil) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, tc.want, ev.Operation) }) } @@ -200,7 +200,7 @@ func TestDecode_DeviceTimeParsing(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { in := msg("tns1:VideoSource/MotionAlarm", "Changed", tc.in, nil, nil) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) if tc.want.IsZero() { assert.True(t, ev.DeviceTime.IsZero(), "DeviceTime=%v", ev.DeviceTime) } else { @@ -215,7 +215,7 @@ func TestDecode_EmptySourceAndDataYieldNilMaps(t *testing.T) { // safely len() and index into Source/Data without nil-checking, but // we do not allocate an empty map for empty notifications. in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", nil, nil) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Nil(t, ev.Source) assert.Nil(t, ev.Data) } @@ -242,7 +242,7 @@ func TestDecode_StateValueIsCaseInsensitive(t *testing.T) { t.Run(tc.name, func(t *testing.T) { in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", nil, map[string]string{"State": tc.value}) - ev := Decode(in, "dev", time.Now()) + ev := decode(in, "dev", time.Now()) assert.Equal(t, tc.want, ev.State) }) } diff --git a/event/stream/doc.go b/event/stream/doc.go index 0223ae1..8121525 100644 --- a/event/stream/doc.go +++ b/event/stream/doc.go @@ -1,13 +1,59 @@ -// Package stream will provide a long-running, channel-based consumer for -// ONVIF device events. It is meant to hide the SOAP/XML, pull-point -// subscription lifecycle, subscription renewal and vendor-specific topic -// conventions behind a typed Event stream. +// Package stream is a typed, channel-based consumer for ONVIF device +// events. It hides the SOAP/XML, pull-point subscription lifecycle, +// subscription renewal and vendor-specific topic conventions behind a +// single Event stream. // -// This file lays down the value types (Kind, State, PropertyOperation, -// Event) and the topic Classifier. The Stream type, its NewStream -// constructor and the Events/Errors channels land in follow-up changes. +// # Usage // -// The package classifies vendor-specific topic strings (AXIS, Hikvision, -// Avigilon, Hanwha, Bosch, Dahua) into a small set of normalized Kind -// values so callers do not need to special-case device manufacturers. +// dev, _ := onvif.NewDevice(onvif.DeviceParams{Xaddr: "...", Username: "...", Password: "..."}) +// s, err := stream.NewStream(ctx, dev, stream.Options{DeviceID: "front-door"}) +// if err != nil { /* construction failed: auth, network, or camera does not advertise events */ } +// defer s.Close() +// +// for ev := range s.Events() { +// switch ev.Kind { +// case stream.KindMotion: +// if ev.State == stream.StateActive { /* start recording */ } +// } +// } +// +// # Invariants +// +// NewStream performs network I/O. It returns once the +// CreatePullPointSubscription call has succeeded; auth and reachability +// failures surface as an error from NewStream rather than landing on +// the Errors channel later. +// +// Two goroutines back each Stream: a pull loop and a renew loop. Both +// exit when the context passed to NewStream is cancelled or when Close +// is called. Close is idempotent and bounded — see Stream.Close. +// +// Events is closed exactly when the Stream stops. Ranging over Events +// is safe; a closed channel terminates the loop without a Close call. +// Errors is also closed at stop time. Both channels are buffered (16 +// slots by default); sends to Errors are non-blocking so a stalled +// consumer drops older errors rather than the pull loop blocking on +// log output. +// +// The decoded Event preserves the wire form (Topic, raw Source and +// Data maps) so callers can fall back to inspecting non-standard +// payloads when Kind is KindUnknown. +// +// # Reconnect +// +// On ReconnectAfterFailures consecutive PullMessages failures the +// Stream silently recreates its pull-point subscription. ONVIF cameras +// replay each property's current value with PropertyInitialized on a +// new subscription; Events delivered between recreate and the first +// non-Initialized event carry Event.AfterReconnect=true so consumers +// can suppress duplicate handling. +// +// Set Options.DisableReconnect=true to opt out of recreate; the pull +// loop will retry against the original subscription until ctx cancel. +// +// # Topic classification +// +// Classify maps ONVIF topic strings to a small set of normalized Kind +// values across AXIS, Hikvision, Avigilon, Hanwha, Bosch and Dahua. See +// topics.go for the verified mapping table with public-doc citations. package stream diff --git a/event/stream/stream.go b/event/stream/stream.go index f10c9c5..520843d 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -31,55 +31,71 @@ const maxResponseBytes = 10 << 20 // elapses, so a missed unsubscribe is at worst cosmetic. const closeUnsubscribeTimeout = 5 * time.Second -// Options configures a Stream. The zero value is usable; defaultOptions -// fills in production-sensible defaults for any unset field. +// Options configures a Stream. +// +// Zero-value policy: every duration / int field treats zero as "use the +// default". To opt out of reconnect entirely set DisableReconnect=true +// (sentinel `ReconnectAfterFailures=0` would otherwise collide with the +// default-injection policy). To get a synchronous (unbuffered) channel +// pair set BufferSize=-1. type Options struct { - // DeviceID identifies the camera in emitted Events. Recommended so a - // single channel can fan in multiple cameras. Empty is allowed. + // DeviceID identifies the camera in emitted Events. Recommended so + // a single channel can fan in multiple cameras. Empty is allowed. DeviceID string - // TopicFilter is the raw ONVIF ConcreteSet TopicExpression filter - // passed to CreatePullPointSubscription. The empty string means no + // RawTopicFilter is the raw ONVIF ConcreteSet TopicExpression + // filter passed to CreatePullPointSubscription. Empty means no // filter — required for AXIS, accepted by every other vendor we - // support. Callers should normally leave this empty and rely on - // Classify for routing. - TopicFilter string - // PullTimeout is the server-side wait time in each PullMessages call - // (xsd:duration). The camera returns early when messages are - // available; otherwise it returns empty after this timeout. Default: - // 5s. + // support. The name carries 'Raw' because the value is fed verbatim + // into the SOAP envelope: callers should normally leave it empty + // and rely on Classify for routing rather than ask the camera to + // filter server-side, which is fragile across vendors. + RawTopicFilter string + // PullTimeout is the server-side wait time in each PullMessages + // call (xsd:duration). The camera returns early when messages are + // available; otherwise it returns empty after this timeout. Zero + // means default (5s). PullTimeout time.Duration // MessageLimit caps the number of NotificationMessage entries - // returned per PullMessages call. Default: 10. + // returned per PullMessages call. Zero means default (32). A busy + // AXIS with many configured inputs can burst beyond 10 per pull; + // 32 covers that without significantly enlarging quiet pulls. MessageLimit int // InitialTermination is the requested subscription lifetime passed // to CreatePullPointSubscription. The renew loop refreshes well - // before this expires. Default: 60s. + // before this expires. Zero means default (60s). InitialTermination time.Duration // RenewMargin is how long before InitialTermination expiry the // renew loop fires. Larger margins tolerate slower networks at the - // cost of more renew SOAP calls. Default: 10s. + // cost of more renew SOAP calls. Zero means default (10s). RenewMargin time.Duration // ReconnectAfterFailures is the consecutive PullMessages failure // count that triggers a CreatePullPointSubscription recreate. The // camera or pull-point can die for many reasons (camera reboot, // subscription garbage-collected after a renew miss, intermediate // NAT timeout); rebuilding the subscription is the only reliable - // recovery. Default: 3. + // recovery. Zero means default (3). To disable reconnect entirely + // set DisableReconnect=true. ReconnectAfterFailures int + // DisableReconnect skips automatic CreatePullPointSubscription + // recreate. The pull loop will continue retrying against the + // original endpoint until ctx is cancelled. Useful for tests or + // callers managing recovery externally. + DisableReconnect bool // RetryBackoff is the initial sleep between a pull/recreate failure // and the next attempt. Recreate failures double this up to a 30s - // ceiling. Default: 1s. + // ceiling. Zero means default (1s). RetryBackoff time.Duration // BufferSize is the buffer size of the Events and Errors channels. // Larger buffers absorb consumer hiccups at the cost of memory. - // Default: 16. + // Zero means default (16); use -1 for unbuffered (synchronous) + // channels. BufferSize int } func defaultOptions() Options { return Options{ PullTimeout: 5 * time.Second, - MessageLimit: 10, + MessageLimit: 32, InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second, ReconnectAfterFailures: 3, @@ -108,11 +124,16 @@ func (o Options) withDefaults() Options { if o.RetryBackoff > 0 { d.RetryBackoff = o.RetryBackoff } - if o.BufferSize > 0 { + // BufferSize: zero -> default; negative -> 0 (unbuffered). + switch { + case o.BufferSize > 0: d.BufferSize = o.BufferSize + case o.BufferSize < 0: + d.BufferSize = 0 } d.DeviceID = o.DeviceID - d.TopicFilter = o.TopicFilter + d.RawTopicFilter = o.RawTopicFilter + d.DisableReconnect = o.DisableReconnect return d } @@ -280,7 +301,7 @@ func (s *Stream) pullLoop(ctx context.Context) { if err != nil { s.surfaceError(ErrPullFailed{Err: err}) failures++ - if failures >= s.opts.ReconnectAfterFailures { + if !s.opts.DisableReconnect && failures >= s.opts.ReconnectAfterFailures { justRecreated, cont := s.attemptRecreate(ctx, &failures, &recreateBackoff) if !cont { return @@ -300,7 +321,7 @@ func (s *Stream) pullLoop(ctx context.Context) { recreateBackoff = s.opts.RetryBackoff observedAt := s.now() for _, m := range msgs { - ev := Decode(m, s.opts.DeviceID, observedAt) + ev := decode(m, s.opts.DeviceID, observedAt) if afterReconnect { ev.AfterReconnect = true // ONVIF replays current state with @@ -399,11 +420,11 @@ func sleepCtx(ctx context.Context, d time.Duration) bool { func createPullPoint(c caller, opts Options) (string, error) { term := xsd.String(durationToXSD(opts.InitialTermination)) req := event.CreatePullPointSubscription{InitialTerminationTime: &term} - if opts.TopicFilter != "" { + if opts.RawTopicFilter != "" { req.Filter = &event.FilterType{ TopicExpression: &event.TopicExpressionType{ Dialect: xsd.String("http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"), - TopicKinds: xsd.String(opts.TopicFilter), + TopicKinds: xsd.String(opts.RawTopicFilter), }, } } diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index 010f10a..b7343fe 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -326,7 +326,7 @@ func TestStream_PullErrorSurfacedOnErrorsChannel(t *testing.T) { func TestStream_OptionsApplyDefaults(t *testing.T) { o := defaultOptions() assert.Equal(t, 5*time.Second, o.PullTimeout) - assert.Equal(t, 10, o.MessageLimit) + assert.Equal(t, 32, o.MessageLimit) assert.Equal(t, 60*time.Second, o.InitialTermination) assert.Equal(t, 16, o.BufferSize) } diff --git a/examples/event/stream/main.go b/examples/event/stream/main.go index a795ed4..da5cc6e 100644 --- a/examples/event/stream/main.go +++ b/examples/event/stream/main.go @@ -73,7 +73,7 @@ func main() { s, err := stream.NewStream(ctx, dev, stream.Options{ DeviceID: *deviceID, - TopicFilter: *filter, + RawTopicFilter: *filter, PullTimeout: *pullTimeout, }) if err != nil { From 94572504fccb2bf77f7fb1b2e2bbcab86677ae6f Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:58:48 +0200 Subject: [PATCH 39/53] style(examples): gofmt alignment for stream example Options literal --- examples/event/stream/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/event/stream/main.go b/examples/event/stream/main.go index da5cc6e..0d0892f 100644 --- a/examples/event/stream/main.go +++ b/examples/event/stream/main.go @@ -72,9 +72,9 @@ func main() { }() s, err := stream.NewStream(ctx, dev, stream.Options{ - DeviceID: *deviceID, + DeviceID: *deviceID, RawTopicFilter: *filter, - PullTimeout: *pullTimeout, + PullTimeout: *pullTimeout, }) if err != nil { log.Fatalf("open stream: %v", err) From 4fd92dd229eb3c5efca2248f91f69746cf5a4fd0 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 14:59:44 +0200 Subject: [PATCH 40/53] feat(event/stream): raise recreate backoff cap and add jitter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous 30-second cap meant a 1000-camera fleet recovering from a switch reboot would generate a sustained 33 RPS of doomed CreatePullPointSubscription traffic against still-booting cameras, and the synchronised retries would arrive in phase. Two changes: * Cap raised to 5 minutes. Single-camera recovery latency goes from '<=30s after camera comes back' to '<=300s', which is fine because by the time we are this deep in backoff the camera has already been unreachable through 6+ attempts (1s, 2s, 4s, 8s, 16s, 30s under the old cap) — the marginal recovery delay is acceptable to avoid the network melt. * Symmetric ±25% jitter on every recreate sleep so synchronised drops (switch reboot, DHCP storm, NTP slew) do not cause synchronised reconnect surges. Standard practice — same shape AWS, Cloudflare and HA event_manager use. Tests assert the jitter range, the documented cap value (so a future maintainer flipping it back to 30s notices in CI), and that jitter varies across calls (proves the rand source is wired). --- event/stream/jitter_test.go | 44 +++++++++++++++++++++++++++++++++++++ event/stream/stream.go | 33 ++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 event/stream/jitter_test.go diff --git a/event/stream/jitter_test.go b/event/stream/jitter_test.go new file mode 100644 index 0000000..3e5d839 --- /dev/null +++ b/event/stream/jitter_test.go @@ -0,0 +1,44 @@ +package stream + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestJitter_StaysWithinFraction(t *testing.T) { + const base = time.Second + low := time.Duration(float64(base) * (1 - jitterFraction)) + high := time.Duration(float64(base) * (1 + jitterFraction)) + for i := 0; i < 200; i++ { + got := jitter(base) + assert.GreaterOrEqual(t, got, low, "iteration %d", i) + assert.LessOrEqual(t, got, high, "iteration %d", i) + } +} + +func TestJitter_ZeroAndNegativeReturnPositive(t *testing.T) { + assert.Greater(t, jitter(0), time.Duration(0)) + assert.Greater(t, jitter(-time.Second), time.Duration(0)) +} + +func TestJitter_VariesAcrossCalls(t *testing.T) { + // Sanity check that we're not returning a constant. Vanishingly + // unlikely to flake (probability ~ (1/uint64-space)^9). + first := jitter(time.Second) + allEqual := true + for i := 0; i < 10; i++ { + if jitter(time.Second) != first { + allEqual = false + break + } + } + assert.False(t, allEqual, "jitter is producing a constant; rand seed not working") +} + +func TestMaxRecreateBackoff_Is5Minutes(t *testing.T) { + // Document the policy choice in a test so a future maintainer + // changing this notices. + assert.Equal(t, 5*time.Minute, maxRecreateBackoff) +} diff --git a/event/stream/stream.go b/event/stream/stream.go index 520843d..b1f22d2 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "math/rand" "net/http" "regexp" "strconv" @@ -138,7 +139,18 @@ func (o Options) withDefaults() Options { } // maxRecreateBackoff caps exponential backoff between recreate attempts. -const maxRecreateBackoff = 30 * time.Second +// Sized for fleet deployments: a 1000-camera setup recovering from a +// switch reboot would otherwise hammer the network with one recreate +// attempt per camera per 30s; 5 minutes gives the network time to +// settle while still recovering promptly when a single camera comes +// back. +const maxRecreateBackoff = 5 * time.Minute + +// jitterFraction is the symmetric jitter applied to recreate backoff: +// the actual sleep is sampled from [backoff*(1-jitter), backoff*(1+jitter)]. +// Prevents thundering-herd reconnects when many cameras drop together +// (switch reboot, NAT timeout). +const jitterFraction = 0.25 // caller is the subset of *onvif.Device the Stream depends on. Tests // substitute a fake; production code uses the device adapter. @@ -351,7 +363,7 @@ func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *ti addr, err := createPullPoint(s.caller, s.opts) if err != nil { s.surfaceError(ErrRecreateFailed{Err: err}) - if !sleepCtx(ctx, *backoff) { + if !sleepCtx(ctx, jitter(*backoff)) { return false, false } *backoff *= 2 @@ -366,6 +378,23 @@ func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *ti return true, true } +// jitter returns d perturbed by ±jitterFraction. Used to spread +// recreate attempts across a fleet so a synchronised drop (switch +// reboot, DHCP storm) does not cause a synchronised reconnect surge. +// Returns at least 1ns to keep sleepCtx happy. +func jitter(d time.Duration) time.Duration { + if d <= 0 { + return time.Nanosecond + } + spread := float64(d) * jitterFraction + delta := (rand.Float64()*2 - 1) * spread + out := time.Duration(float64(d) + delta) + if out <= 0 { + out = time.Nanosecond + } + return out +} + // renewLoop refreshes the subscription before InitialTermination expires. // Exits when ctx is cancelled. func (s *Stream) renewLoop(ctx context.Context) { From c6cad2c35d6c7ca03008cff75a9b89487c92ffe1 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 15:01:35 +0200 Subject: [PATCH 41/53] test(event/stream): close review-2 coverage gaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the missing test coverage flagged by the test-rigor reviewer. Coverage / behaviour -------------------- * TestClose_ReturnsUnsubscribeError: previously closeErr plumbing was effectively dead code in the suite. Inject an Unsubscribe failure and assert the error wraps it. * TestNewStream_CtxAlreadyCancelled: pins the behaviour for a pre-cancelled parent context (construction succeeds because createPullPoint does not consult ctx; run goroutine exits immediately and Events closes). * TestStream_DisableReconnectKeepsRetryingOriginalEndpoint: proves the new opt-out actually disables CreatePullPoint recreate. * TestStream_RecreateResetsFailuresAndBackoffOnSuccess: locks the attemptRecreate success path resetting *failures and *backoff so a later failure does not accidentally enter exponential backoff immediately. Race detection -------------- * TestStream_PullPointMutationVisibleToRenewLoopUnderRace: drives the pullPoint write-by-pullLoop / read-by-renewLoop race so -race actually exercises the mutex critical sections. Previously the mutex was structurally correct but no test produced contention. Decoder edge cases ------------------ * TestDecode_PropertyOperationIsCaseSensitive: per WS-Notification §3.3, values are PascalCase. Lowercased forms fall through to PropertyUnknown. * TestDecode_StateValueTrimsWhitespace: explicit assertions for ' true ', tabs, newlines and whitespace-only. * TestDecode_SimpleItemEmptyValueIsUnknownState: empty value yields StateUnknown but the empty entry is still preserved in Data map. * TestDecode_DeviceTimeAdditionalLayouts: the +0200 compact offset and naked-no-TZ formats added in the hardening commit. * TestDecode_DeviceTimeStillRejectsNonsense: the broader layout list did not start accepting garbage. * TestExtractState_FirstBooleanLikeWins: uses explicit slice construction (pair{k,v} -> SimpleItem) so the assertion does not depend on map iteration order, the latent flake risk in the AOA test pointed out by the reviewer. Helpers ------- * helpers_test.go waitFor(t, d, msg, cond) centralises the 10ms-poll-until-deadline pattern that previously appeared four times across stream_test / renew_test / reconnect_test. * TestFakeCaller_QueueThenDefaultFallback: self-test for the fake. When the fake grows to 100+ LOC, debugging a flaky stream test should not also require investigating whether the fake itself behaves correctly. --- event/stream/coverage_test.go | 295 ++++++++++++++++++++++++++++++++++ event/stream/helpers_test.go | 21 +++ 2 files changed, 316 insertions(+) create mode 100644 event/stream/coverage_test.go create mode 100644 event/stream/helpers_test.go diff --git a/event/stream/coverage_test.go b/event/stream/coverage_test.go new file mode 100644 index 0000000..29b7ea2 --- /dev/null +++ b/event/stream/coverage_test.go @@ -0,0 +1,295 @@ +package stream + +import ( + "context" + "errors" + "strings" + "testing" + "time" + + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --- Close surfaces unsubscribe error -------------------------------- + +func TestClose_ReturnsUnsubscribeError(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // Default empty pulls keep the loop running. Override default + // SendSoap to fail so Close's Unsubscribe also fails. + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errors.New("simulated transport failure")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) + require.NoError(t, err) + + err = s.Close() + require.Error(t, err) + assert.Contains(t, err.Error(), "unsubscribe pull point") + assert.Contains(t, err.Error(), "simulated transport failure") +} + +// --- NewStream against already-cancelled context ---------------------- + +func TestNewStream_CtxAlreadyCancelled(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() // cancel before NewStream + + s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) + // Create-pull-point doesn't currently consult ctx (it uses caller + // directly), so construction succeeds and the run goroutine exits + // immediately. Close must still work cleanly. + require.NoError(t, err) + require.NotNil(t, s) + + // Events channel must close promptly because the goroutine exits. + select { + case _, ok := <-s.Events(): + assert.False(t, ok, "events channel should be closed when ctx is pre-cancelled") + case <-time.After(time.Second): + t.Fatal("events channel was not closed within 1s") + } + _ = s.Close() +} + +// --- DisableReconnect honours the opt-out ---------------------------- + +func TestStream_DisableReconnectKeepsRetryingOriginalEndpoint(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // All pulls fail; default SendSoap stays as empty-pull (success) + // only if the fake's queue exhausts — we override default to a + // failure so EVERY pull errors. + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + DisableReconnect: true, + }) + require.NoError(t, err) + defer s.Close() + + // Let the loop spin for a bit, then assert no second CallMethod + // (recreate would invoke CallMethod, which we are watching). + time.Sleep(200 * time.Millisecond) + fc.mu.Lock() + calls := len(fc.callMethodCalls) + fc.mu.Unlock() + assert.Equal(t, 1, calls, "DisableReconnect must prevent recreate; got %d CallMethod calls", calls) +} + +// --- Recreate resets failures+backoff on success --------------------- + +func TestStream_RecreateResetsFailuresAndBackoffOnSuccess(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueCallMethod(createPullPointRespAlt, nil) + // Pull fails once -> triggers recreate -> recreate succeeds -> + // next pull succeeds. After that we should NOT see another + // recreate (failures was reset). Provide enough successful empty + // pulls. + fc.queueSendSoap("", errors.New("first failure")) + // Subsequent pulls succeed via default empty pull. + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + time.Sleep(200 * time.Millisecond) + fc.mu.Lock() + calls := len(fc.callMethodCalls) + fc.mu.Unlock() + assert.Equal(t, 2, calls, + "after one failure + successful recreate, no further recreates expected; got %d", calls) +} + +// --- pullPointMu under race ------------------------------------------ + +func TestStream_PullPointMutationVisibleToRenewLoopUnderRace(t *testing.T) { + // Drives the pullPoint write-by-pullLoop / read-by-renewLoop race + // so -race actually exercises the mutex critical sections. With + // short termination and quick recreate, renew is firing alongside + // the recreate write. + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + // Queue a stream of alt-response recreates so each retry installs + // a new pullPoint. + for i := 0; i < 50; i++ { + fc.queueCallMethod(createPullPointRespAlt, nil) + } + // Default empty pulls. + // Force pull errors so reconnect path fires repeatedly: override + // default and queue mostly-failing pulls. + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errors.New("recurring pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 5 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 1 * time.Millisecond, + InitialTermination: 20 * time.Millisecond, + RenewMargin: 2 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + // Spin for ~300ms; the race detector will fire if either + // pullPointMu critical section is broken. We don't assert on + // content here — the value is the -race signal. + time.Sleep(300 * time.Millisecond) +} + +// --- fakeCaller self-test -------------------------------------------- + +func TestFakeCaller_QueueThenDefaultFallback(t *testing.T) { + fc := newFakeCaller() + fc.queueSendSoap("first", nil) + fc.queueSendSoap("second", nil) + // Default already set to an empty pull response. + + r1, err := fc.SendSoap("ep", "body") + require.NoError(t, err) + b1 := make([]byte, 10) + n, _ := r1.Body.Read(b1) + assert.Equal(t, "first", string(b1[:n])) + + r2, _ := fc.SendSoap("ep", "body") + b2 := make([]byte, 10) + n, _ = r2.Body.Read(b2) + assert.Equal(t, "second", string(b2[:n])) + + // Queue is exhausted; default kicks in. + r3, err := fc.SendSoap("ep", "body") + require.NoError(t, err) + require.NotNil(t, r3) + b3 := make([]byte, 2048) + n, _ = r3.Body.Read(b3) + assert.Contains(t, string(b3[:n]), "PullMessagesResponse", + "default SendSoap should be an empty PullMessagesResponse envelope") +} + +// --- Decoder coverage gaps ------------------------------------------- + +func TestDecode_PropertyOperationIsCaseSensitive(t *testing.T) { + // Per WS-Notification §3.3 PropertyOperation values are + // 'Initialized' / 'Changed' / 'Deleted'. Lowercased forms in the + // wild are malformed and should fall through to PropertyUnknown. + in := msg("tns1:VideoSource/MotionAlarm", "changed", "", nil, nil) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, PropertyUnknown, ev.Operation) +} + +func TestDecode_StateValueTrimsWhitespace(t *testing.T) { + tests := []struct { + name string + value string + want State + }{ + {"leading_trailing", " true ", StateActive}, + {"tab_newline", "\ttrue\n", StateActive}, + {"only_spaces", " ", StateUnknown}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", + nil, map[string]string{"State": tc.value}) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, tc.want, ev.State) + }) + } +} + +func TestDecode_SimpleItemEmptyValueIsUnknownState(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", + nil, map[string]string{"State": ""}) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, StateUnknown, ev.State) + // Empty value still preserved in the Data map. + v, ok := ev.Data["State"] + assert.True(t, ok) + assert.Equal(t, "", v) +} + +func TestDecode_DeviceTimeAdditionalLayouts(t *testing.T) { + tests := []struct { + name string + in string + want time.Time + }{ + {"compact_offset", "2026-05-21T12:30:00+0200", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, + {"compact_offset_subsec", "2026-05-21T12:30:00.500+0200", time.Date(2026, 5, 21, 10, 30, 0, 500_000_000, time.UTC)}, + {"naked_no_tz", "2026-05-21T10:30:00", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", tc.in, nil, nil) + ev := decode(in, "dev", time.Now()) + assert.True(t, ev.DeviceTime.Equal(tc.want), + "input=%q got=%v want=%v", tc.in, ev.DeviceTime, tc.want) + }) + } +} + +// --- extractState deterministic order with explicit slice ------------ + +func TestExtractState_FirstBooleanLikeWins(t *testing.T) { + // Verifies the documented behaviour: when multiple Data items have + // boolean-like values, the first by slice order wins. + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", nil, nil) + in.Message.Message.Data.SimpleItem = simpleItemsFromPairs([]pair{ + {"ObjectId", "42"}, + {"State", "true"}, + {"Trailer", "false"}, + }) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, StateActive, ev.State, + "first boolean-like value (State=true) must win, not Trailer=false") +} + +type pair struct{ k, v string } + +func simpleItemsFromPairs(pairs []pair) []event.SimpleItem { + out := make([]event.SimpleItem, len(pairs)) + for i, p := range pairs { + out[i] = event.SimpleItem{ + Name: xsd.AnyType(p.k), + Value: xsd.AnyType(p.v), + } + } + return out +} + +// --- ensure the new layouts don't accept unrelated junk -------------- + +func TestDecode_DeviceTimeStillRejectsNonsense(t *testing.T) { + for _, s := range []string{"hello", "2026-13-45T99:99:99", strings.Repeat("9", 50)} { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", s, nil, nil) + ev := decode(in, "dev", time.Now()) + assert.True(t, ev.DeviceTime.IsZero(), "input=%q should yield zero, got %v", s, ev.DeviceTime) + } +} diff --git a/event/stream/helpers_test.go b/event/stream/helpers_test.go new file mode 100644 index 0000000..41dab80 --- /dev/null +++ b/event/stream/helpers_test.go @@ -0,0 +1,21 @@ +package stream + +import ( + "testing" + "time" +) + +// waitFor polls cond at 10ms intervals up to d. Fails the test with msg +// if cond never returns true. Centralises the pattern that appears in +// renew/reconnect/stream tests so retries are uniform. +func waitFor(t *testing.T, d time.Duration, msg string, cond func() bool) { + t.Helper() + deadline := time.Now().Add(d) + for time.Now().Before(deadline) { + if cond() { + return + } + time.Sleep(10 * time.Millisecond) + } + t.Fatalf("waitFor timed out after %s: %s", d, msg) +} From 1ceef725ecd8e74a70e8353ff3973e90f2f262da Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 15:02:23 +0200 Subject: [PATCH 42/53] fix(examples): secure credential handling and Errors-arm bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two findings from the resource/security and API reviews of the streamtest example. Credentials ----------- * loadPassword resolves the camera password in order: 1. ONVIF_PASSWORD environment variable (recommended). 2. -password-file (newline trimmed). 3. Interactive prompt when nothing else is set. * -password flag still works but now logs a WARNING that the value leaks into shell history and process listings. Documented as 'INSECURE' in the flag help. * Updated package godoc with a Credentials section. Errors-arm bug -------------- * Previous code: case e := <-s.Errors() with no ok check. When the Stream closed, this arm would spin on a closed channel printing '' forever (until ctx-done elsewhere unblocked it). Mirror the Events arm's ok pattern. * Switched the error-log branch to inspect the typed errors added in the previous commit: ErrRecreateFailed gets a louder 'camera may be offline' log line; ErrPullFailed is a quieter 'will retry' since the loop handles transient pull errors automatically. Also prints '[after-reconnect]' on events carrying that flag so the operator can see when the stream silently recovered a dropped subscription — confirms the new observability surface is useful at the CLI level. --- examples/event/stream/main.go | 89 +++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/examples/event/stream/main.go b/examples/event/stream/main.go index 0d0892f..cc6174b 100644 --- a/examples/event/stream/main.go +++ b/examples/event/stream/main.go @@ -3,24 +3,36 @@ // classifier against real-camera topics; not intended as a production // tool. // -// Example: +// # Usage // // go run ./examples/event/stream \ // -xaddr 192.168.1.10 \ -// -username root -password admin \ +// -username root \ // -duration 60s // -// The xaddr is the camera's host or host:port (the library appends -// /onvif/device_service); pass with no protocol prefix. +// # Credentials +// +// The camera password is read, in order of preference: +// +// 1. The ONVIF_PASSWORD environment variable. +// 2. A file pointed at by -password-file (newline stripped). +// 3. Interactive prompt when stdin is a tty. +// +// -password is also accepted but DISCOURAGED — it leaks the credential +// into shell history and the system process listing. Use only for +// throwaway dev cameras. package main import ( + "bufio" "context" + "errors" "flag" "fmt" "log" "os" "os/signal" + "strings" "syscall" "time" @@ -31,14 +43,15 @@ import ( func main() { xaddr := flag.String("xaddr", "", "camera host or host:port (required)") username := flag.String("username", "", "ONVIF user (required)") - password := flag.String("password", "", "ONVIF password (required)") + insecurePassword := flag.String("password", "", "INSECURE — leaks into shell history; prefer ONVIF_PASSWORD env or -password-file") + passwordFile := flag.String("password-file", "", "read password from this file (newline trimmed)") deviceID := flag.String("device-id", "", "logical name printed with each event (default: xaddr)") filter := flag.String("filter", "", "raw ONVIF ConcreteSet topic filter (empty = all topics, works on AXIS)") pullTimeout := flag.Duration("pull-timeout", 5*time.Second, "server-side wait per PullMessages call") duration := flag.Duration("duration", 0, "stop after this long (0 = run until Ctrl-C)") flag.Parse() - if *xaddr == "" || *username == "" || *password == "" { + if *xaddr == "" || *username == "" { flag.Usage() os.Exit(2) } @@ -46,10 +59,15 @@ func main() { *deviceID = *xaddr } + password, err := loadPassword(*insecurePassword, *passwordFile) + if err != nil { + log.Fatalf("password: %v", err) + } + dev, err := onvif.NewDevice(onvif.DeviceParams{ Xaddr: *xaddr, Username: *username, - Password: *password, + Password: password, AuthMode: onvif.UsernameTokenAuth, }) if err != nil { @@ -79,7 +97,11 @@ func main() { if err != nil { log.Fatalf("open stream: %v", err) } - defer s.Close() + defer func() { + if err := s.Close(); err != nil { + log.Printf("stream close: %v", err) + } + }() log.Printf("streaming from %s (device-id=%s, filter=%q)", *xaddr, *deviceID, *filter) for { @@ -93,6 +115,9 @@ func main() { } fmt.Printf("%s kind=%-15s state=%-9s op=%-12s topic=%s", ev.Timestamp.Format(time.RFC3339), ev.Kind, ev.State, ev.Operation, ev.Topic) + if ev.AfterReconnect { + fmt.Print(" [after-reconnect]") + } if len(ev.Source) > 0 { fmt.Printf(" source=%v", ev.Source) } @@ -100,8 +125,52 @@ func main() { fmt.Printf(" data=%v", ev.Data) } fmt.Println() - case e := <-s.Errors(): - log.Printf("stream error: %v", e) + case e, ok := <-s.Errors(): + if !ok { + return + } + var pull stream.ErrPullFailed + var recreate stream.ErrRecreateFailed + switch { + case errors.As(e, &recreate): + log.Printf("RECREATE failed: %v (camera may be offline)", recreate.Err) + case errors.As(e, &pull): + log.Printf("pull error (will retry): %v", pull.Err) + default: + log.Printf("stream error: %v", e) + } } } } + +// loadPassword resolves the camera password from the environment first +// (ONVIF_PASSWORD), then -password-file, then an interactive prompt as +// a last resort. The insecure -password flag is honoured only if +// nothing else is set, and a warning is logged. +func loadPassword(insecure, file string) (string, error) { + if env := os.Getenv("ONVIF_PASSWORD"); env != "" { + return env, nil + } + if file != "" { + b, err := os.ReadFile(file) + if err != nil { + return "", fmt.Errorf("read %s: %w", file, err) + } + return strings.TrimRight(string(b), "\r\n"), nil + } + if insecure != "" { + log.Print("WARNING: -password leaks into shell history and process listings; prefer ONVIF_PASSWORD env or -password-file") + return insecure, nil + } + // Interactive prompt — works when stdin is a tty. We use a plain + // reader (rather than golang.org/x/term hidden input) to keep + // this example dependency-free; in production, callers should + // integrate term.ReadPassword. + fmt.Fprint(os.Stderr, "ONVIF password (visible): ") + r := bufio.NewReader(os.Stdin) + line, err := r.ReadString('\n') + if err != nil { + return "", errors.New("no password supplied (set ONVIF_PASSWORD, -password-file, or pipe input)") + } + return strings.TrimRight(line, "\r\n"), nil +} From a1fc7832efdc998c60ac2f1a2359b34eb69699ae Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 15:14:40 +0200 Subject: [PATCH 43/53] refactor(event/stream): align source and test files 1:1 by concern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously stream.go was a 621-line monolith holding the Stream type, SOAP plumbing, pull loop, renew loop, recreate logic and jitter. The test side had grown five orphan files (renew_test.go, reconnect_test.go, soap_test.go, jitter_test.go, coverage_test.go) with no matching source files. The mismatch made it harder than necessary to find the code that backed a given test. This commit splits stream.go by concern so each source file has its own test file alongside it. Files <100 LOC (errors, jitter) were folded into their conceptual parents rather than left as fragments. New layout — 8 source + 8 test + helpers (test utility) + doc: stream.go <-> stream_test.go Stream type, Options, lifecycle soap.go <-> soap_test.go SOAP plumbing + fault detection renew.go <-> renew_test.go Renew loop and absolute time reconnect.go <-> reconnect_test.go Pull loop, recreate, jitter decode.go <-> decode_test.go NotificationMessage -> Event types.go <-> types_test.go Event types + typed errors topics.go <-> topics_test.go Classifier table doc.go Package godoc landing page helpers_test.go waitFor (test-only utility) Mergers ------- * errors.go (typed error wrappers, 42 LOC) -> types.go. ErrPullFailed / ErrRenewFailed / ErrRecreateFailed are part of the type system, not a separate concern. * jitter.go (40 LOC) -> reconnect.go. jitter is an implementation detail of attemptRecreate, used nowhere else. Test distribution ----------------- * coverage_test.go was a catch-all; tests moved to the file matching the function under test: - Close*, NewStream_*, FakeCaller_* -> stream_test.go - DisableReconnect_*, RecreateResets_*, PullPointMutation_* -> reconnect_test.go - Decode_*, ExtractState_* -> decode_test.go * soap_test.go shed the two orphans that did not belong there: - TestRenew_SendsAbsoluteDateTimeNotDuration -> renew_test.go - TestClose_BoundedByTimeoutOnHungUnsubscribe -> stream_test.go * errors_test.go's pure type tests -> types_test.go * errors_test.go's Stream-integration tests -> reconnect_test.go * jitter_test.go -> reconnect_test.go No behaviour change. Test suite passes -race clean. --- event/stream/coverage_test.go | 295 ----------------------------- event/stream/decode_test.go | 101 ++++++++++ event/stream/errors.go | 42 ----- event/stream/errors_test.go | 133 ------------- event/stream/jitter_test.go | 44 ----- event/stream/reconnect.go | 127 +++++++++++++ event/stream/reconnect_test.go | 215 ++++++++++++++++++++- event/stream/renew.go | 64 +++++++ event/stream/renew_test.go | 36 ++++ event/stream/soap.go | 189 +++++++++++++++++++ event/stream/soap_test.go | 89 +-------- event/stream/stream.go | 332 +-------------------------------- event/stream/stream_test.go | 93 +++++++++ event/stream/types.go | 38 ++++ event/stream/types_test.go | 27 +++ 15 files changed, 893 insertions(+), 932 deletions(-) delete mode 100644 event/stream/coverage_test.go delete mode 100644 event/stream/errors.go delete mode 100644 event/stream/errors_test.go delete mode 100644 event/stream/jitter_test.go create mode 100644 event/stream/reconnect.go create mode 100644 event/stream/renew.go create mode 100644 event/stream/soap.go diff --git a/event/stream/coverage_test.go b/event/stream/coverage_test.go deleted file mode 100644 index 29b7ea2..0000000 --- a/event/stream/coverage_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package stream - -import ( - "context" - "errors" - "strings" - "testing" - "time" - - "github.com/kerberos-io/onvif/event" - "github.com/kerberos-io/onvif/xsd" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// --- Close surfaces unsubscribe error -------------------------------- - -func TestClose_ReturnsUnsubscribeError(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - // Default empty pulls keep the loop running. Override default - // SendSoap to fail so Close's Unsubscribe also fails. - fc.mu.Lock() - fc.defaultSendSoap = fakeResp{err: errors.New("simulated transport failure")} - fc.mu.Unlock() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) - require.NoError(t, err) - - err = s.Close() - require.Error(t, err) - assert.Contains(t, err.Error(), "unsubscribe pull point") - assert.Contains(t, err.Error(), "simulated transport failure") -} - -// --- NewStream against already-cancelled context ---------------------- - -func TestNewStream_CtxAlreadyCancelled(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() // cancel before NewStream - - s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) - // Create-pull-point doesn't currently consult ctx (it uses caller - // directly), so construction succeeds and the run goroutine exits - // immediately. Close must still work cleanly. - require.NoError(t, err) - require.NotNil(t, s) - - // Events channel must close promptly because the goroutine exits. - select { - case _, ok := <-s.Events(): - assert.False(t, ok, "events channel should be closed when ctx is pre-cancelled") - case <-time.After(time.Second): - t.Fatal("events channel was not closed within 1s") - } - _ = s.Close() -} - -// --- DisableReconnect honours the opt-out ---------------------------- - -func TestStream_DisableReconnectKeepsRetryingOriginalEndpoint(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - // All pulls fail; default SendSoap stays as empty-pull (success) - // only if the fake's queue exhausts — we override default to a - // failure so EVERY pull errors. - fc.mu.Lock() - fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} - fc.mu.Unlock() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - PullTimeout: 10 * time.Millisecond, - RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, - DisableReconnect: true, - }) - require.NoError(t, err) - defer s.Close() - - // Let the loop spin for a bit, then assert no second CallMethod - // (recreate would invoke CallMethod, which we are watching). - time.Sleep(200 * time.Millisecond) - fc.mu.Lock() - calls := len(fc.callMethodCalls) - fc.mu.Unlock() - assert.Equal(t, 1, calls, "DisableReconnect must prevent recreate; got %d CallMethod calls", calls) -} - -// --- Recreate resets failures+backoff on success --------------------- - -func TestStream_RecreateResetsFailuresAndBackoffOnSuccess(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - fc.queueCallMethod(createPullPointRespAlt, nil) - // Pull fails once -> triggers recreate -> recreate succeeds -> - // next pull succeeds. After that we should NOT see another - // recreate (failures was reset). Provide enough successful empty - // pulls. - fc.queueSendSoap("", errors.New("first failure")) - // Subsequent pulls succeed via default empty pull. - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - PullTimeout: 10 * time.Millisecond, - ReconnectAfterFailures: 1, - RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, - }) - require.NoError(t, err) - defer s.Close() - - time.Sleep(200 * time.Millisecond) - fc.mu.Lock() - calls := len(fc.callMethodCalls) - fc.mu.Unlock() - assert.Equal(t, 2, calls, - "after one failure + successful recreate, no further recreates expected; got %d", calls) -} - -// --- pullPointMu under race ------------------------------------------ - -func TestStream_PullPointMutationVisibleToRenewLoopUnderRace(t *testing.T) { - // Drives the pullPoint write-by-pullLoop / read-by-renewLoop race - // so -race actually exercises the mutex critical sections. With - // short termination and quick recreate, renew is firing alongside - // the recreate write. - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - // Queue a stream of alt-response recreates so each retry installs - // a new pullPoint. - for i := 0; i < 50; i++ { - fc.queueCallMethod(createPullPointRespAlt, nil) - } - // Default empty pulls. - // Force pull errors so reconnect path fires repeatedly: override - // default and queue mostly-failing pulls. - fc.mu.Lock() - fc.defaultSendSoap = fakeResp{err: errors.New("recurring pull fail")} - fc.mu.Unlock() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - PullTimeout: 5 * time.Millisecond, - ReconnectAfterFailures: 1, - RetryBackoff: 1 * time.Millisecond, - InitialTermination: 20 * time.Millisecond, - RenewMargin: 2 * time.Millisecond, - }) - require.NoError(t, err) - defer s.Close() - - // Spin for ~300ms; the race detector will fire if either - // pullPointMu critical section is broken. We don't assert on - // content here — the value is the -race signal. - time.Sleep(300 * time.Millisecond) -} - -// --- fakeCaller self-test -------------------------------------------- - -func TestFakeCaller_QueueThenDefaultFallback(t *testing.T) { - fc := newFakeCaller() - fc.queueSendSoap("first", nil) - fc.queueSendSoap("second", nil) - // Default already set to an empty pull response. - - r1, err := fc.SendSoap("ep", "body") - require.NoError(t, err) - b1 := make([]byte, 10) - n, _ := r1.Body.Read(b1) - assert.Equal(t, "first", string(b1[:n])) - - r2, _ := fc.SendSoap("ep", "body") - b2 := make([]byte, 10) - n, _ = r2.Body.Read(b2) - assert.Equal(t, "second", string(b2[:n])) - - // Queue is exhausted; default kicks in. - r3, err := fc.SendSoap("ep", "body") - require.NoError(t, err) - require.NotNil(t, r3) - b3 := make([]byte, 2048) - n, _ = r3.Body.Read(b3) - assert.Contains(t, string(b3[:n]), "PullMessagesResponse", - "default SendSoap should be an empty PullMessagesResponse envelope") -} - -// --- Decoder coverage gaps ------------------------------------------- - -func TestDecode_PropertyOperationIsCaseSensitive(t *testing.T) { - // Per WS-Notification §3.3 PropertyOperation values are - // 'Initialized' / 'Changed' / 'Deleted'. Lowercased forms in the - // wild are malformed and should fall through to PropertyUnknown. - in := msg("tns1:VideoSource/MotionAlarm", "changed", "", nil, nil) - ev := decode(in, "dev", time.Now()) - assert.Equal(t, PropertyUnknown, ev.Operation) -} - -func TestDecode_StateValueTrimsWhitespace(t *testing.T) { - tests := []struct { - name string - value string - want State - }{ - {"leading_trailing", " true ", StateActive}, - {"tab_newline", "\ttrue\n", StateActive}, - {"only_spaces", " ", StateUnknown}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", - nil, map[string]string{"State": tc.value}) - ev := decode(in, "dev", time.Now()) - assert.Equal(t, tc.want, ev.State) - }) - } -} - -func TestDecode_SimpleItemEmptyValueIsUnknownState(t *testing.T) { - in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", - nil, map[string]string{"State": ""}) - ev := decode(in, "dev", time.Now()) - assert.Equal(t, StateUnknown, ev.State) - // Empty value still preserved in the Data map. - v, ok := ev.Data["State"] - assert.True(t, ok) - assert.Equal(t, "", v) -} - -func TestDecode_DeviceTimeAdditionalLayouts(t *testing.T) { - tests := []struct { - name string - in string - want time.Time - }{ - {"compact_offset", "2026-05-21T12:30:00+0200", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, - {"compact_offset_subsec", "2026-05-21T12:30:00.500+0200", time.Date(2026, 5, 21, 10, 30, 0, 500_000_000, time.UTC)}, - {"naked_no_tz", "2026-05-21T10:30:00", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - in := msg("tns1:VideoSource/MotionAlarm", "Changed", tc.in, nil, nil) - ev := decode(in, "dev", time.Now()) - assert.True(t, ev.DeviceTime.Equal(tc.want), - "input=%q got=%v want=%v", tc.in, ev.DeviceTime, tc.want) - }) - } -} - -// --- extractState deterministic order with explicit slice ------------ - -func TestExtractState_FirstBooleanLikeWins(t *testing.T) { - // Verifies the documented behaviour: when multiple Data items have - // boolean-like values, the first by slice order wins. - in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", nil, nil) - in.Message.Message.Data.SimpleItem = simpleItemsFromPairs([]pair{ - {"ObjectId", "42"}, - {"State", "true"}, - {"Trailer", "false"}, - }) - ev := decode(in, "dev", time.Now()) - assert.Equal(t, StateActive, ev.State, - "first boolean-like value (State=true) must win, not Trailer=false") -} - -type pair struct{ k, v string } - -func simpleItemsFromPairs(pairs []pair) []event.SimpleItem { - out := make([]event.SimpleItem, len(pairs)) - for i, p := range pairs { - out[i] = event.SimpleItem{ - Name: xsd.AnyType(p.k), - Value: xsd.AnyType(p.v), - } - } - return out -} - -// --- ensure the new layouts don't accept unrelated junk -------------- - -func TestDecode_DeviceTimeStillRejectsNonsense(t *testing.T) { - for _, s := range []string{"hello", "2026-13-45T99:99:99", strings.Repeat("9", 50)} { - in := msg("tns1:VideoSource/MotionAlarm", "Changed", s, nil, nil) - ev := decode(in, "dev", time.Now()) - assert.True(t, ev.DeviceTime.IsZero(), "input=%q should yield zero, got %v", s, ev.DeviceTime) - } -} diff --git a/event/stream/decode_test.go b/event/stream/decode_test.go index 2d52800..f30eec7 100644 --- a/event/stream/decode_test.go +++ b/event/stream/decode_test.go @@ -1,6 +1,7 @@ package stream import ( + "strings" "testing" "time" @@ -247,3 +248,103 @@ func TestDecode_StateValueIsCaseInsensitive(t *testing.T) { }) } } + +// --- Edge cases for state extraction and time parsing ---------------- + +func TestDecode_PropertyOperationIsCaseSensitive(t *testing.T) { + // Per WS-Notification §3.3 PropertyOperation values are + // 'Initialized' / 'Changed' / 'Deleted'. Lowercased forms are + // malformed and should fall through to PropertyUnknown. + in := msg("tns1:VideoSource/MotionAlarm", "changed", "", nil, nil) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, PropertyUnknown, ev.Operation) +} + +func TestDecode_StateValueTrimsWhitespace(t *testing.T) { + tests := []struct { + name string + value string + want State + }{ + {"leading_trailing", " true ", StateActive}, + {"tab_newline", "\ttrue\n", StateActive}, + {"only_spaces", " ", StateUnknown}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", + nil, map[string]string{"State": tc.value}) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, tc.want, ev.State) + }) + } +} + +func TestDecode_SimpleItemEmptyValueIsUnknownState(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", + nil, map[string]string{"State": ""}) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, StateUnknown, ev.State) + v, ok := ev.Data["State"] + assert.True(t, ok) + assert.Equal(t, "", v) +} + +func TestDecode_DeviceTimeAdditionalLayouts(t *testing.T) { + tests := []struct { + name string + in string + want time.Time + }{ + {"compact_offset", "2026-05-21T12:30:00+0200", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, + {"compact_offset_subsec", "2026-05-21T12:30:00.500+0200", time.Date(2026, 5, 21, 10, 30, 0, 500_000_000, time.UTC)}, + {"naked_no_tz", "2026-05-21T10:30:00", time.Date(2026, 5, 21, 10, 30, 0, 0, time.UTC)}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", tc.in, nil, nil) + ev := decode(in, "dev", time.Now()) + assert.True(t, ev.DeviceTime.Equal(tc.want), + "input=%q got=%v want=%v", tc.in, ev.DeviceTime, tc.want) + }) + } +} + +func TestDecode_DeviceTimeStillRejectsNonsense(t *testing.T) { + for _, s := range []string{"hello", "2026-13-45T99:99:99", strings.Repeat("9", 50)} { + in := msg("tns1:VideoSource/MotionAlarm", "Changed", s, nil, nil) + ev := decode(in, "dev", time.Now()) + assert.True(t, ev.DeviceTime.IsZero(), "input=%q should yield zero, got %v", s, ev.DeviceTime) + } +} + +// --- First-boolean-wins with explicit slice order -------------------- + +type pair struct{ k, v string } + +func simpleItemsFromPairs(pairs []pair) []event.SimpleItem { + out := make([]event.SimpleItem, len(pairs)) + for i, p := range pairs { + out[i] = event.SimpleItem{ + Name: xsd.AnyType(p.k), + Value: xsd.AnyType(p.v), + } + } + return out +} + +func TestExtractState_FirstBooleanLikeWins(t *testing.T) { + // Documented behaviour: when multiple Data items have boolean-like + // values, the first by slice order wins. Use explicit slice + // construction so the assertion does not depend on map iteration + // order. + in := msg("tns1:VideoSource/MotionAlarm", "Changed", "", nil, nil) + in.Message.Message.Data.SimpleItem = simpleItemsFromPairs([]pair{ + {"ObjectId", "42"}, + {"State", "true"}, + {"Trailer", "false"}, + }) + ev := decode(in, "dev", time.Now()) + assert.Equal(t, StateActive, ev.State, + "first boolean-like value (State=true) must win, not Trailer=false") +} diff --git a/event/stream/errors.go b/event/stream/errors.go deleted file mode 100644 index bd80674..0000000 --- a/event/stream/errors.go +++ /dev/null @@ -1,42 +0,0 @@ -package stream - -import "fmt" - -// Op identifies which Stream operation failed. Used by ErrPullFailed, -// ErrRenewFailed and ErrRecreateFailed so consumers can branch with -// errors.As without parsing the wrapped message. -type Op string - -const ( - OpPull Op = "pull" - OpRenew Op = "renew" - OpRecreate Op = "recreate" -) - -// ErrPullFailed wraps a transient PullMessages failure. The pull loop -// surfaces it on the Errors channel and continues. Consumers can match -// with errors.As(err, &stream.ErrPullFailed{}) — see -// TestErrors_TypedAssertion. -type ErrPullFailed struct{ Err error } - -func (e ErrPullFailed) Error() string { return fmt.Sprintf("pull messages: %v", e.Err) } -func (e ErrPullFailed) Unwrap() error { return e.Err } -func (ErrPullFailed) Op() Op { return OpPull } - -// ErrRenewFailed wraps a Renew SOAP failure. Renew errors are usually -// recovered implicitly: the subscription dies, pull starts failing, and -// the reconnect logic recreates it. -type ErrRenewFailed struct{ Err error } - -func (e ErrRenewFailed) Error() string { return fmt.Sprintf("renew pull point: %v", e.Err) } -func (e ErrRenewFailed) Unwrap() error { return e.Err } -func (ErrRenewFailed) Op() Op { return OpRenew } - -// ErrRecreateFailed wraps a failed CreatePullPointSubscription during -// the reconnect path. The loop continues with exponential backoff; -// consumers seeing this repeatedly should consider the camera offline. -type ErrRecreateFailed struct{ Err error } - -func (e ErrRecreateFailed) Error() string { return fmt.Sprintf("recreate pull point: %v", e.Err) } -func (e ErrRecreateFailed) Unwrap() error { return e.Err } -func (ErrRecreateFailed) Op() Op { return OpRecreate } diff --git a/event/stream/errors_test.go b/event/stream/errors_test.go deleted file mode 100644 index d68e347..0000000 --- a/event/stream/errors_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package stream - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTypedErrors_UnwrapAndOp(t *testing.T) { - inner := errors.New("boom") - tests := []struct { - name string - err error - op Op - }{ - {"pull", ErrPullFailed{Err: inner}, OpPull}, - {"renew", ErrRenewFailed{Err: inner}, OpRenew}, - {"recreate", ErrRecreateFailed{Err: inner}, OpRecreate}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - assert.True(t, errors.Is(tc.err, inner), "errors.Is should unwrap to inner") - assert.Contains(t, tc.err.Error(), "boom") - - // Each typed error exposes Op() for branch-without-string-parse. - if e, ok := tc.err.(interface{ Op() Op }); ok { - assert.Equal(t, tc.op, e.Op()) - } else { - t.Fatalf("%T does not expose Op()", tc.err) - } - }) - } -} - -func TestStream_PullErrorIsTypedErrPullFailed(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - fc.queueSendSoap("", errors.New("transient")) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - PullTimeout: 50 * time.Millisecond, - RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, - }) - require.NoError(t, err) - defer s.Close() - - select { - case e := <-s.Errors(): - var pullErr ErrPullFailed - require.True(t, errors.As(e, &pullErr), "expected ErrPullFailed, got %T: %v", e, e) - assert.Contains(t, pullErr.Err.Error(), "transient") - case <-time.After(time.Second): - t.Fatal("expected ErrPullFailed on Errors channel") - } -} - -func TestStream_RecreateErrorIsTypedErrRecreateFailed(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - fc.mu.Lock() - fc.defaultCall = fakeResp{err: errors.New("recreate fail")} - fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} - fc.mu.Unlock() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - PullTimeout: 10 * time.Millisecond, - ReconnectAfterFailures: 1, - RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, - }) - require.NoError(t, err) - defer s.Close() - - deadline := time.Now().Add(time.Second) - var sawRecreate bool - for time.Now().Before(deadline) && !sawRecreate { - select { - case e := <-s.Errors(): - var rec ErrRecreateFailed - if errors.As(e, &rec) { - sawRecreate = true - assert.Contains(t, rec.Err.Error(), "recreate fail") - } - case <-time.After(50 * time.Millisecond): - } - } - assert.True(t, sawRecreate, "expected at least one ErrRecreateFailed on Errors") -} - -func TestStream_AfterReconnectFlagSetOnFirstPostRecreateBatch(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - // Second create is the recreate. - fc.queueCallMethod(createPullPointRespAlt, nil) - - // First pull fails -> triggers recreate with ReconnectAfterFailures=1. - fc.queueSendSoap("", errors.New("transient")) - // First pull after recreate: a Changed motion event. The flag - // should be true, and should clear (because we received a - // non-Initialized event). - fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) - // Second pull after recreate: another motion event. Flag should - // now be false. - fc.queueSendSoap(pullMessagesResp(motionMsg("false")), nil) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - PullTimeout: 50 * time.Millisecond, - ReconnectAfterFailures: 1, - RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, - }) - require.NoError(t, err) - defer s.Close() - - ev1 := receive(t, s.Events(), 2*time.Second) - assert.True(t, ev1.AfterReconnect, "first event after recreate must carry AfterReconnect=true") - assert.Equal(t, StateActive, ev1.State) - - ev2 := receive(t, s.Events(), 2*time.Second) - assert.False(t, ev2.AfterReconnect, "subsequent events should not carry AfterReconnect") - assert.Equal(t, StateInactive, ev2.State) -} diff --git a/event/stream/jitter_test.go b/event/stream/jitter_test.go deleted file mode 100644 index 3e5d839..0000000 --- a/event/stream/jitter_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package stream - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestJitter_StaysWithinFraction(t *testing.T) { - const base = time.Second - low := time.Duration(float64(base) * (1 - jitterFraction)) - high := time.Duration(float64(base) * (1 + jitterFraction)) - for i := 0; i < 200; i++ { - got := jitter(base) - assert.GreaterOrEqual(t, got, low, "iteration %d", i) - assert.LessOrEqual(t, got, high, "iteration %d", i) - } -} - -func TestJitter_ZeroAndNegativeReturnPositive(t *testing.T) { - assert.Greater(t, jitter(0), time.Duration(0)) - assert.Greater(t, jitter(-time.Second), time.Duration(0)) -} - -func TestJitter_VariesAcrossCalls(t *testing.T) { - // Sanity check that we're not returning a constant. Vanishingly - // unlikely to flake (probability ~ (1/uint64-space)^9). - first := jitter(time.Second) - allEqual := true - for i := 0; i < 10; i++ { - if jitter(time.Second) != first { - allEqual = false - break - } - } - assert.False(t, allEqual, "jitter is producing a constant; rand seed not working") -} - -func TestMaxRecreateBackoff_Is5Minutes(t *testing.T) { - // Document the policy choice in a test so a future maintainer - // changing this notices. - assert.Equal(t, 5*time.Minute, maxRecreateBackoff) -} diff --git a/event/stream/reconnect.go b/event/stream/reconnect.go new file mode 100644 index 0000000..52cb047 --- /dev/null +++ b/event/stream/reconnect.go @@ -0,0 +1,127 @@ +package stream + +import ( + "context" + "math/rand" + "time" +) + +// maxRecreateBackoff caps exponential backoff between recreate attempts. +// Sized for fleet deployments: a 1000-camera setup recovering from a +// switch reboot would otherwise hammer the network with one recreate +// attempt per camera per 30s; 5 minutes gives the network time to +// settle while still recovering promptly when a single camera comes +// back. +const maxRecreateBackoff = 5 * time.Minute + +// jitterFraction is the symmetric jitter applied to recreate backoff: +// the actual sleep is sampled from [backoff*(1-jitter), backoff*(1+jitter)]. +// Prevents thundering-herd reconnects when many cameras drop together +// (switch reboot, NAT timeout). +const jitterFraction = 0.25 + +// pullLoop is the main pull goroutine of a Stream. It calls +// PullMessages in a tight loop, decodes results into Events and feeds +// the Events channel. +// +// After ReconnectAfterFailures consecutive pull errors it asks +// attemptRecreate to recreate the pull-point subscription, marking the +// next batch's events with AfterReconnect so consumers can suppress +// duplicate handling of the ONVIF Initialized-replay that follows a +// new subscription. +// +// Exits when ctx is cancelled. +func (s *Stream) pullLoop(ctx context.Context) { + var failures int + recreateBackoff := s.opts.RetryBackoff + var afterReconnect bool + + for { + if ctx.Err() != nil { + return + } + msgs, err := pullMessages(s.caller, s.getPullPoint(), s.opts) + if err != nil { + s.surfaceError(ErrPullFailed{Err: err}) + failures++ + if !s.opts.DisableReconnect && failures >= s.opts.ReconnectAfterFailures { + justRecreated, cont := s.attemptRecreate(ctx, &failures, &recreateBackoff) + if !cont { + return + } + if justRecreated { + afterReconnect = true + } + continue + } + if !sleepCtx(ctx, s.opts.RetryBackoff) { + return + } + continue + } + // Successful pull resets failure tracking. + failures = 0 + recreateBackoff = s.opts.RetryBackoff + observedAt := s.now() + for _, m := range msgs { + ev := decode(m, s.opts.DeviceID, observedAt) + if afterReconnect { + ev.AfterReconnect = true + // ONVIF replays current state with + // PropertyInitialized on a new subscription. + // Clear the flag as soon as we see anything + // other than Initialized — at that point we + // have transitioned to live events. + if ev.Operation != PropertyInitialized { + afterReconnect = false + } + } + select { + case <-ctx.Done(): + return + case s.events <- ev: + } + } + } +} + +// attemptRecreate calls CreatePullPointSubscription and on success +// installs the new endpoint atomically. The first return is true when +// recreate succeeded just now (caller flags the next batch with +// AfterReconnect). The second return is false only if ctx was cancelled +// during backoff (caller should exit the run loop). +func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) (justRecreated, cont bool) { + addr, err := createPullPoint(s.caller, s.opts) + if err != nil { + s.surfaceError(ErrRecreateFailed{Err: err}) + if !sleepCtx(ctx, jitter(*backoff)) { + return false, false + } + *backoff *= 2 + if *backoff > maxRecreateBackoff { + *backoff = maxRecreateBackoff + } + return false, true + } + s.setPullPoint(addr) + *failures = 0 + *backoff = s.opts.RetryBackoff + return true, true +} + +// jitter returns d perturbed by ±jitterFraction. Used to spread +// recreate attempts across a fleet so a synchronised drop (switch +// reboot, DHCP storm) does not cause a synchronised reconnect surge. +// Returns at least 1ns to keep sleepCtx happy. +func jitter(d time.Duration) time.Duration { + if d <= 0 { + return time.Nanosecond + } + spread := float64(d) * jitterFraction + delta := (rand.Float64()*2 - 1) * spread + out := time.Duration(float64(d) + delta) + if out <= 0 { + out = time.Nanosecond + } + return out +} diff --git a/event/stream/reconnect_test.go b/event/stream/reconnect_test.go index 9162768..4dfbb5e 100644 --- a/event/stream/reconnect_test.go +++ b/event/stream/reconnect_test.go @@ -29,15 +29,13 @@ const createPullPointRespAlt = ` ` +// --- Recreate after pull failures ------------------------------------ + func TestStream_RecreatesSubscriptionAfterRepeatedPullErrors(t *testing.T) { fc := newFakeCaller() - // Initial subscription. fc.queueCallMethod(createPullPointResp, nil) - // Recreated subscription returns a *different* endpoint. fc.queueCallMethod(createPullPointRespAlt, nil) - // First pull fails. With ReconnectAfterFailures=1 this triggers a - // recreate; subsequent pulls go to PullSub_2 which we'll observe. fc.queueSendSoap("", errors.New("transient failure")) fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) @@ -48,7 +46,7 @@ func TestStream_RecreatesSubscriptionAfterRepeatedPullErrors(t *testing.T) { PullTimeout: 50 * time.Millisecond, ReconnectAfterFailures: 1, RetryBackoff: 10 * time.Millisecond, - InitialTermination: 30 * time.Second, // keep renew quiet + InitialTermination: 30 * time.Second, }) require.NoError(t, err) defer s.Close() @@ -60,8 +58,6 @@ func TestStream_RecreatesSubscriptionAfterRepeatedPullErrors(t *testing.T) { defer fc.mu.Unlock() require.Len(t, fc.callMethodCalls, 2, "expected exactly 2 CallMethod calls (initial + recreate)") - // The PullMessages call that delivered the motion event must - // target the new endpoint. var newEndpointPulls int for _, c := range fc.sendSoapCalls { if c[0] == "http://camera.local/onvif/Events/PullSub_2" { @@ -75,9 +71,6 @@ func TestStream_RecreatesSubscriptionAfterRepeatedPullErrors(t *testing.T) { func TestStream_BackoffWhenRecreateFails(t *testing.T) { fc := newFakeCaller() fc.queueCallMethod(createPullPointResp, nil) - // After the initial successful create, every CallMethod (recreate) - // and SendSoap (pull) fails. The loop should keep retrying with - // exponential backoff rather than blocking forever or spinning. fc.mu.Lock() fc.defaultCall = fakeResp{err: errors.New("recreate fail")} fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} @@ -118,3 +111,205 @@ func TestStream_RetryBackoffDefault(t *testing.T) { o := defaultOptions() assert.Equal(t, time.Second, o.RetryBackoff) } + +func TestStream_DisableReconnectKeepsRetryingOriginalEndpoint(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + DisableReconnect: true, + }) + require.NoError(t, err) + defer s.Close() + + time.Sleep(200 * time.Millisecond) + fc.mu.Lock() + calls := len(fc.callMethodCalls) + fc.mu.Unlock() + assert.Equal(t, 1, calls, "DisableReconnect must prevent recreate; got %d CallMethod calls", calls) +} + +func TestStream_RecreateResetsFailuresAndBackoffOnSuccess(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueCallMethod(createPullPointRespAlt, nil) + fc.queueSendSoap("", errors.New("first failure")) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + time.Sleep(200 * time.Millisecond) + fc.mu.Lock() + calls := len(fc.callMethodCalls) + fc.mu.Unlock() + assert.Equal(t, 2, calls, + "after one failure + successful recreate, no further recreates expected; got %d", calls) +} + +func TestStream_PullPointMutationVisibleToRenewLoopUnderRace(t *testing.T) { + // Drives the pullPoint write-by-pullLoop / read-by-renewLoop race + // so -race actually exercises the mutex critical sections. + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + for i := 0; i < 50; i++ { + fc.queueCallMethod(createPullPointRespAlt, nil) + } + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errors.New("recurring pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 5 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 1 * time.Millisecond, + InitialTermination: 20 * time.Millisecond, + RenewMargin: 2 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + time.Sleep(300 * time.Millisecond) +} + +// --- Typed errors from the reconnect path ---------------------------- + +func TestStream_PullErrorIsTypedErrPullFailed(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueSendSoap("", errors.New("transient")) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 50 * time.Millisecond, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + select { + case e := <-s.Errors(): + var pullErr ErrPullFailed + require.True(t, errors.As(e, &pullErr), "expected ErrPullFailed, got %T: %v", e, e) + assert.Contains(t, pullErr.Err.Error(), "transient") + case <-time.After(time.Second): + t.Fatal("expected ErrPullFailed on Errors channel") + } +} + +func TestStream_RecreateErrorIsTypedErrRecreateFailed(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.mu.Lock() + fc.defaultCall = fakeResp{err: errors.New("recreate fail")} + fc.defaultSendSoap = fakeResp{err: errors.New("pull fail")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 10 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(time.Second) + var sawRecreate bool + for time.Now().Before(deadline) && !sawRecreate { + select { + case e := <-s.Errors(): + var rec ErrRecreateFailed + if errors.As(e, &rec) { + sawRecreate = true + assert.Contains(t, rec.Err.Error(), "recreate fail") + } + case <-time.After(50 * time.Millisecond): + } + } + assert.True(t, sawRecreate, "expected at least one ErrRecreateFailed on Errors") +} + +func TestStream_AfterReconnectFlagSetOnFirstPostRecreateBatch(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.queueCallMethod(createPullPointRespAlt, nil) + + fc.queueSendSoap("", errors.New("transient")) + fc.queueSendSoap(pullMessagesResp(motionMsg("true")), nil) + fc.queueSendSoap(pullMessagesResp(motionMsg("false")), nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 50 * time.Millisecond, + ReconnectAfterFailures: 1, + RetryBackoff: 10 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + defer s.Close() + + ev1 := receive(t, s.Events(), 2*time.Second) + assert.True(t, ev1.AfterReconnect, "first event after recreate must carry AfterReconnect=true") + assert.Equal(t, StateActive, ev1.State) + + ev2 := receive(t, s.Events(), 2*time.Second) + assert.False(t, ev2.AfterReconnect, "subsequent events should not carry AfterReconnect") + assert.Equal(t, StateInactive, ev2.State) +} + +// --- Jitter ---------------------------------------------------------- + +func TestJitter_StaysWithinFraction(t *testing.T) { + const base = time.Second + low := time.Duration(float64(base) * (1 - jitterFraction)) + high := time.Duration(float64(base) * (1 + jitterFraction)) + for i := 0; i < 200; i++ { + got := jitter(base) + assert.GreaterOrEqual(t, got, low, "iteration %d", i) + assert.LessOrEqual(t, got, high, "iteration %d", i) + } +} + +func TestJitter_ZeroAndNegativeReturnPositive(t *testing.T) { + assert.Greater(t, jitter(0), time.Duration(0)) + assert.Greater(t, jitter(-time.Second), time.Duration(0)) +} + +func TestJitter_VariesAcrossCalls(t *testing.T) { + first := jitter(time.Second) + allEqual := true + for i := 0; i < 10; i++ { + if jitter(time.Second) != first { + allEqual = false + break + } + } + assert.False(t, allEqual, "jitter is producing a constant; rand seed not working") +} + +func TestMaxRecreateBackoff_Is5Minutes(t *testing.T) { + assert.Equal(t, 5*time.Minute, maxRecreateBackoff) +} diff --git a/event/stream/renew.go b/event/stream/renew.go new file mode 100644 index 0000000..6a8763a --- /dev/null +++ b/event/stream/renew.go @@ -0,0 +1,64 @@ +package stream + +import ( + "context" + "encoding/xml" + "fmt" + "time" + + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" +) + +// renewLoop refreshes the subscription before InitialTermination expires. +// Exits when ctx is cancelled. Renew failures surface as ErrRenewFailed +// on the Errors channel; the loop continues because a permanently +// failing renew will eventually drop the subscription and the pull +// loop's reconnect path will recover (recreate is the only reliable +// recovery once a subscription is GC'd at the camera). +func (s *Stream) renewLoop(ctx context.Context) { + interval := s.opts.InitialTermination - s.opts.RenewMargin + if interval <= 0 { + // Pathological config (margin >= termination): fall back to + // renewing at half the termination so we still refresh, + // rather than busy-looping or never renewing. + interval = s.opts.InitialTermination / 2 + if interval <= 0 { + interval = time.Second + } + } + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if err := renewPullPoint(s.caller, s.getPullPoint(), s.opts); err != nil { + s.surfaceError(ErrRenewFailed{Err: err}) + } + } + } +} + +// renewPullPoint issues a wsnt:Renew SOAP against the given +// subscription endpoint with an absolute TerminationTime. +// +// WS-BaseNotification §6.1.1 declares TerminationTime as xsd:dateTime +// OR xsd:duration, but older Hikvision, some Dahua and some Bosch +// firmwares reject the relative-duration form. We send an absolute +// UTC datetime to match what production NVRs do. +func renewPullPoint(c caller, endpoint string, opts Options) error { + absoluteEnd := time.Now().UTC().Add(opts.InitialTermination).Format("2006-01-02T15:04:05Z") + req := event.Renew{TerminationTime: xsd.String(absoluteEnd)} + body, err := xml.Marshal(req) + if err != nil { + return fmt.Errorf("marshal Renew: %w", err) + } + resp, err := c.SendSoap(endpoint, string(body)) + if err != nil { + return err + } + _, err = readClose(resp) + return err +} diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go index 20e2146..cc758bb 100644 --- a/event/stream/renew_test.go +++ b/event/stream/renew_test.go @@ -132,3 +132,39 @@ func TestStream_RenewErrorSurfacedOnErrorsChannel(t *testing.T) { type errInjected struct{} func (errInjected) Error() string { return "injected fake error" } + +func TestRenew_SendsAbsoluteDateTimeNotDuration(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + InitialTermination: 30 * time.Millisecond, + RenewMargin: 5 * time.Millisecond, + }) + require.NoError(t, err) + defer s.Close() + + deadline := time.Now().Add(500 * time.Millisecond) + for time.Now().Before(deadline) { + if countSendSoapMatching(fc, "Renew") >= 1 { + break + } + time.Sleep(10 * time.Millisecond) + } + + fc.mu.Lock() + defer fc.mu.Unlock() + var renewBody string + for _, c := range fc.sendSoapCalls { + if strings.Contains(c[1], "Renew") { + renewBody = c[1] + break + } + } + require.NotEmpty(t, renewBody, "no Renew call observed") + // Absolute form is "YYYY-MM-DDTHH:MM:SSZ" not "PTnS". + assert.NotContains(t, renewBody, "PT", "Renew should not send relative duration; some firmwares reject it") + assert.Regexp(t, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`, renewBody, "Renew should send absolute RFC3339 UTC") +} diff --git a/event/stream/soap.go b/event/stream/soap.go new file mode 100644 index 0000000..4d9bcf6 --- /dev/null +++ b/event/stream/soap.go @@ -0,0 +1,189 @@ +package stream + +import ( + "bytes" + "encoding/xml" + "errors" + "fmt" + "io" + "net/http" + "regexp" + "strconv" + "strings" + "time" + + "github.com/kerberos-io/onvif/event" + "github.com/kerberos-io/onvif/xsd" +) + +// maxResponseBytes caps the size of a SOAP response we will buffer in +// memory. ONVIF PullMessages bodies are normally <100KB even with dense +// analytics payloads; 10 MiB is comfortably above legitimate traffic +// while keeping a hostile or buggy camera from OOMing the process. +const maxResponseBytes = 10 << 20 + +// createPullPoint issues a CreatePullPointSubscription against the +// device service. Returns the SubscriptionReference Address, which is +// the endpoint subsequent PullMessages / Renew / Unsubscribe calls +// target. +func createPullPoint(c caller, opts Options) (string, error) { + term := xsd.String(durationToXSD(opts.InitialTermination)) + req := event.CreatePullPointSubscription{InitialTerminationTime: &term} + if opts.RawTopicFilter != "" { + req.Filter = &event.FilterType{ + TopicExpression: &event.TopicExpressionType{ + Dialect: xsd.String("http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"), + TopicKinds: xsd.String(opts.RawTopicFilter), + }, + } + } + resp, err := c.CallMethod(req) + if err != nil { + return "", err + } + body, err := readClose(resp) + if err != nil { + return "", err + } + var decoded event.CreatePullPointSubscriptionResponse + if err := unmarshalNode(body, "CreatePullPointSubscriptionResponse", &decoded); err != nil { + return "", err + } + addr := string(decoded.SubscriptionReference.Address) + if addr == "" { + return "", errors.New("CreatePullPointSubscription response has empty SubscriptionReference Address") + } + return addr, nil +} + +// pullMessages issues PullMessages against an active subscription +// endpoint and returns the decoded NotificationMessage list. Empty +// slice (not error) when the camera had nothing within PullTimeout. +func pullMessages(c caller, endpoint string, opts Options) ([]event.NotificationMessage, error) { + req := event.PullMessages{ + Timeout: xsd.Duration(durationToXSD(opts.PullTimeout)), + MessageLimit: xsd.Int(opts.MessageLimit), + } + body, err := xml.Marshal(req) + if err != nil { + return nil, fmt.Errorf("marshal PullMessages: %w", err) + } + resp, err := c.SendSoap(endpoint, string(body)) + if err != nil { + return nil, err + } + respBody, err := readClose(resp) + if err != nil { + return nil, err + } + var decoded event.PullMessagesResponse + if err := unmarshalNode(respBody, "PullMessagesResponse", &decoded); err != nil { + return nil, err + } + return decoded.NotificationMessage, nil +} + +// unsubscribePullPoint sends a best-effort Unsubscribe to release the +// subscription server-side. Empty endpoint is a no-op (the construction +// failed before installing one). +func unsubscribePullPoint(c caller, endpoint string) error { + if endpoint == "" { + return nil + } + body, err := xml.Marshal(event.Unsubscribe{}) + if err != nil { + return fmt.Errorf("marshal Unsubscribe: %w", err) + } + resp, err := c.SendSoap(endpoint, string(body)) + if err != nil { + return err + } + _, err = readClose(resp) + return err +} + +// readClose reads at most maxResponseBytes from resp.Body and closes +// it. LimitReader prevents a hostile or buggy camera from OOMing the +// agent by streaming an unbounded response. +func readClose(resp *http.Response) (string, error) { + if resp == nil || resp.Body == nil { + return "", errors.New("nil HTTP response") + } + defer resp.Body.Close() + b, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) + if err != nil { + return "", fmt.Errorf("read response body: %w", err) + } + return string(b), nil +} + +// unmarshalNode finds the first XML start element with the given local +// name and decodes it into out. ONVIF SOAP responses come wrapped in an +// envelope with multiple namespace prefixes; this helper sidesteps +// namespace matching by keying on local name only. +// +// When the camera returns a SOAP Fault instead of the expected +// response, the fault reason is surfaced as the error so callers can +// distinguish "auth failed" / "subscription expired" from "unparseable +// response". +func unmarshalNode(body, localName string, out any) error { + if reason := extractSOAPFault(body); reason != "" { + return fmt.Errorf("ONVIF SOAP fault: %s", reason) + } + dec := xml.NewDecoder(bytes.NewBufferString(body)) + for { + tok, err := dec.Token() + if err != nil { + if errors.Is(err, io.EOF) { + return fmt.Errorf("ONVIF response missing %s element", localName) + } + return fmt.Errorf("scan ONVIF response: %w", err) + } + start, ok := tok.(xml.StartElement) + if !ok { + continue + } + if start.Name.Local != localName { + continue + } + if err := dec.DecodeElement(out, &start); err != nil { + return fmt.Errorf("decode %s: %w", localName, err) + } + return nil + } +} + +var ( + // SOAP 1.1: reason + soap11FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?faultstring[^>]*>(.*?)\s]+:)?faultstring>`) + // SOAP 1.2: ...reason... + soap12FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Reason\b[^>]*>.*?<(?:[^:>\s]+:)?Text[^>]*>(.*?)\s]+:)?Text>`) +) + +// extractSOAPFault returns the human-readable reason text from a SOAP +// fault, or empty string when the body is not a fault. Handles both +// SOAP 1.1 (faultstring) and SOAP 1.2 (Reason/Text) shapes. +func extractSOAPFault(body string) string { + if !strings.Contains(body, "Fault") { + return "" + } + if m := soap11FaultRE.FindStringSubmatch(body); len(m) > 1 { + return strings.TrimSpace(m[1]) + } + if m := soap12FaultRE.FindStringSubmatch(body); len(m) > 1 { + return strings.TrimSpace(m[1]) + } + return "" +} + +// durationToXSD formats a Go time.Duration as an xsd:duration string in +// PTnS form. Second precision is sufficient — ONVIF cameras do not +// honour sub-second pull timeouts and intermediate routers may round in +// any case. +func durationToXSD(d time.Duration) string { + secs := int(d.Round(time.Second).Seconds()) + if secs <= 0 { + secs = 1 + } + return "PT" + strconv.Itoa(secs) + "S" +} diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index e8c48f5..7c5b6c2 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -4,7 +4,6 @@ import ( "context" "strings" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -59,97 +58,29 @@ func TestUnmarshalNode_ReturnsFaultReasonInsteadOfMissingElement(t *testing.T) { assert.NotContains(t, err.Error(), "missing PullMessagesResponse") } -// --- Renew sends absolute datetime ----------------------------------- - -func TestRenew_SendsAbsoluteDateTimeNotDuration(t *testing.T) { - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{ - InitialTermination: 30 * time.Millisecond, - RenewMargin: 5 * time.Millisecond, - }) - require.NoError(t, err) - defer s.Close() - - deadline := time.Now().Add(500 * time.Millisecond) - for time.Now().Before(deadline) { - if countSendSoapMatching(fc, "Renew") >= 1 { - break - } - time.Sleep(10 * time.Millisecond) - } - - fc.mu.Lock() - defer fc.mu.Unlock() - var renewBody string - for _, c := range fc.sendSoapCalls { - if strings.Contains(c[1], "Renew") { - renewBody = c[1] - break - } - } - require.NotEmpty(t, renewBody, "no Renew call observed") - // Absolute form is "YYYY-MM-DDTHH:MM:SSZ" not "PTnS". - assert.NotContains(t, renewBody, "PT", "Renew should not send relative duration; some firmwares reject it") - assert.Regexp(t, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`, renewBody, "Renew should send absolute RFC3339 UTC") -} - // --- Bounded body read ----------------------------------------------- func TestReadClose_LimitsBodySize(t *testing.T) { - // Build a response with a body just over the limit. readClose must - // not return more than the limit even if the camera pretends to - // send more. if maxResponseBytes < 1024 { t.Skip("limit too small for this test") } big := strings.Repeat("A", maxResponseBytes+1024) - // Wrap in a minimal SOAP envelope so the body is at least - // well-formed shape-wise. body := "" + big + "" fc := newFakeCaller() fc.queueCallMethod(body, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // Construction will fail because the truncated body has no - // CreatePullPointSubscriptionResponse — that's fine; what matters - // is the read completes without OOM. - _, err := newStream(ctx, fc, Options{}) + // CreatePullPointSubscriptionResponse — that's fine; what matters is + // the read completes without OOM. + _, err := newStream(testContext(t), fc, Options{}) assert.Error(t, err) } -// --- Close timeout --------------------------------------------------- - -func TestClose_BoundedByTimeoutOnHungUnsubscribe(t *testing.T) { - // Patch closeUnsubscribeTimeout for the duration of the test so the - // assertion completes promptly. We can't change the const at runtime - // so we use a short InitialTermination and verify Close still - // returns within closeUnsubscribeTimeout + slack rather than - // blocking forever. - fc := newFakeCaller() - fc.queueCallMethod(createPullPointResp, nil) - block := make(chan struct{}) - defer close(block) // release the hung Unsubscribe so the fake's goroutine exits - fc.mu.Lock() - fc.blockUnsubscribe = block - fc.mu.Unlock() - +// testContext returns a Background context already wired to cancel via +// t.Cleanup so the test does not need to manage the cancellation +// goroutine inline. +func testContext(t *testing.T) context.Context { + t.Helper() ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) - require.NoError(t, err) - - start := time.Now() - err = s.Close() - elapsed := time.Since(start) - // Unsubscribe is hung, so Close must surface a timeout error from - // the bounded wait rather than block forever. closeUnsubscribeTimeout - // is 5s; allow 1s slack for scheduling. - require.Error(t, err) - assert.Contains(t, err.Error(), "timeout") - assert.Less(t, elapsed, closeUnsubscribeTimeout+time.Second, - "Close exceeded bound (%s); expected ~%s", elapsed, closeUnsubscribeTimeout) + t.Cleanup(cancel) + return ctx } diff --git a/event/stream/stream.go b/event/stream/stream.go index b1f22d2..4f11415 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -1,31 +1,15 @@ package stream import ( - "bytes" "context" - "encoding/xml" - "errors" "fmt" - "io" - "math/rand" "net/http" - "regexp" - "strconv" - "strings" "sync" "time" "github.com/kerberos-io/onvif" - "github.com/kerberos-io/onvif/event" - "github.com/kerberos-io/onvif/xsd" ) -// maxResponseBytes caps the size of a SOAP response we will buffer in -// memory. ONVIF PullMessages bodies are normally <100KB even with dense -// analytics payloads; 10 MiB is comfortably above legitimate traffic -// while keeping a hostile or buggy camera from OOMing the process. -const maxResponseBytes = 10 << 20 - // closeUnsubscribeTimeout bounds the SOAP Unsubscribe call issued by // Close so a hung camera connection cannot wedge the caller. The // subscription expires at the camera anyway once InitialTermination @@ -138,20 +122,6 @@ func (o Options) withDefaults() Options { return d } -// maxRecreateBackoff caps exponential backoff between recreate attempts. -// Sized for fleet deployments: a 1000-camera setup recovering from a -// switch reboot would otherwise hammer the network with one recreate -// attempt per camera per 30s; 5 minutes gives the network time to -// settle while still recovering promptly when a single camera comes -// back. -const maxRecreateBackoff = 5 * time.Minute - -// jitterFraction is the symmetric jitter applied to recreate backoff: -// the actual sleep is sampled from [backoff*(1-jitter), backoff*(1+jitter)]. -// Prevents thundering-herd reconnects when many cameras drop together -// (switch reboot, NAT timeout). -const jitterFraction = 0.25 - // caller is the subset of *onvif.Device the Stream depends on. Tests // substitute a fake; production code uses the device adapter. // @@ -282,6 +252,8 @@ func (s *Stream) Close() error { return s.closeErr } +// run orchestrates the pull and renew goroutines and closes the +// emission channels once both have exited. func (s *Stream) run(ctx context.Context) { var wg sync.WaitGroup wg.Add(1) @@ -300,130 +272,8 @@ func (s *Stream) run(ctx context.Context) { close(s.done) } -func (s *Stream) pullLoop(ctx context.Context) { - var failures int - recreateBackoff := s.opts.RetryBackoff - var afterReconnect bool - - for { - if ctx.Err() != nil { - return - } - msgs, err := pullMessages(s.caller, s.getPullPoint(), s.opts) - if err != nil { - s.surfaceError(ErrPullFailed{Err: err}) - failures++ - if !s.opts.DisableReconnect && failures >= s.opts.ReconnectAfterFailures { - justRecreated, cont := s.attemptRecreate(ctx, &failures, &recreateBackoff) - if !cont { - return - } - if justRecreated { - afterReconnect = true - } - continue - } - if !sleepCtx(ctx, s.opts.RetryBackoff) { - return - } - continue - } - // Successful pull resets failure tracking. - failures = 0 - recreateBackoff = s.opts.RetryBackoff - observedAt := s.now() - for _, m := range msgs { - ev := decode(m, s.opts.DeviceID, observedAt) - if afterReconnect { - ev.AfterReconnect = true - // ONVIF replays current state with - // PropertyInitialized on a new subscription. - // Clear the flag as soon as we see anything - // other than Initialized — at that point we - // have transitioned to live events. - if ev.Operation != PropertyInitialized { - afterReconnect = false - } - } - select { - case <-ctx.Done(): - return - case s.events <- ev: - } - } - } -} - -// attemptRecreate calls CreatePullPointSubscription and on success -// installs the new endpoint atomically. The first return is true when -// recreate succeeded just now (caller flags the next batch with -// AfterReconnect). The second return is false only if ctx was cancelled -// during backoff (caller should exit the run loop). -func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) (justRecreated, cont bool) { - addr, err := createPullPoint(s.caller, s.opts) - if err != nil { - s.surfaceError(ErrRecreateFailed{Err: err}) - if !sleepCtx(ctx, jitter(*backoff)) { - return false, false - } - *backoff *= 2 - if *backoff > maxRecreateBackoff { - *backoff = maxRecreateBackoff - } - return false, true - } - s.setPullPoint(addr) - *failures = 0 - *backoff = s.opts.RetryBackoff - return true, true -} - -// jitter returns d perturbed by ±jitterFraction. Used to spread -// recreate attempts across a fleet so a synchronised drop (switch -// reboot, DHCP storm) does not cause a synchronised reconnect surge. -// Returns at least 1ns to keep sleepCtx happy. -func jitter(d time.Duration) time.Duration { - if d <= 0 { - return time.Nanosecond - } - spread := float64(d) * jitterFraction - delta := (rand.Float64()*2 - 1) * spread - out := time.Duration(float64(d) + delta) - if out <= 0 { - out = time.Nanosecond - } - return out -} - -// renewLoop refreshes the subscription before InitialTermination expires. -// Exits when ctx is cancelled. -func (s *Stream) renewLoop(ctx context.Context) { - interval := s.opts.InitialTermination - s.opts.RenewMargin - if interval <= 0 { - // Pathological config (margin >= termination): fall back to - // renewing at half the termination so we still refresh, - // rather than busy-looping or never renewing. - interval = s.opts.InitialTermination / 2 - if interval <= 0 { - interval = time.Second - } - } - ticker := time.NewTicker(interval) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - if err := renewPullPoint(s.caller, s.getPullPoint(), s.opts); err != nil { - s.surfaceError(ErrRenewFailed{Err: err}) - } - } - } -} - // surfaceError sends err on the errors channel non-blockingly so a -// stalled consumer cannot block the pull loop. +// stalled consumer cannot block the pull or renew loop. func (s *Stream) surfaceError(err error) { select { case s.errors <- err: @@ -443,179 +293,3 @@ func sleepCtx(ctx context.Context, d time.Duration) bool { return true } } - -// --- SOAP helpers (unexported) ---------------------------------------- - -func createPullPoint(c caller, opts Options) (string, error) { - term := xsd.String(durationToXSD(opts.InitialTermination)) - req := event.CreatePullPointSubscription{InitialTerminationTime: &term} - if opts.RawTopicFilter != "" { - req.Filter = &event.FilterType{ - TopicExpression: &event.TopicExpressionType{ - Dialect: xsd.String("http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"), - TopicKinds: xsd.String(opts.RawTopicFilter), - }, - } - } - resp, err := c.CallMethod(req) - if err != nil { - return "", err - } - body, err := readClose(resp) - if err != nil { - return "", err - } - var decoded event.CreatePullPointSubscriptionResponse - if err := unmarshalNode(body, "CreatePullPointSubscriptionResponse", &decoded); err != nil { - return "", err - } - addr := string(decoded.SubscriptionReference.Address) - if addr == "" { - return "", errors.New("CreatePullPointSubscription response has empty SubscriptionReference Address") - } - return addr, nil -} - -func pullMessages(c caller, endpoint string, opts Options) ([]event.NotificationMessage, error) { - req := event.PullMessages{ - Timeout: xsd.Duration(durationToXSD(opts.PullTimeout)), - MessageLimit: xsd.Int(opts.MessageLimit), - } - body, err := xml.Marshal(req) - if err != nil { - return nil, fmt.Errorf("marshal PullMessages: %w", err) - } - resp, err := c.SendSoap(endpoint, string(body)) - if err != nil { - return nil, err - } - respBody, err := readClose(resp) - if err != nil { - return nil, err - } - var decoded event.PullMessagesResponse - if err := unmarshalNode(respBody, "PullMessagesResponse", &decoded); err != nil { - return nil, err - } - return decoded.NotificationMessage, nil -} - -func renewPullPoint(c caller, endpoint string, opts Options) error { - // WS-BaseNotification §6.1.1 declares TerminationTime as - // xsd:dateTime OR xsd:duration, but older Hikvision, some Dahua - // and some Bosch firmwares reject the relative-duration form. Send - // an absolute UTC datetime to match what production NVRs do. - absoluteEnd := time.Now().UTC().Add(opts.InitialTermination).Format("2006-01-02T15:04:05Z") - req := event.Renew{TerminationTime: xsd.String(absoluteEnd)} - body, err := xml.Marshal(req) - if err != nil { - return fmt.Errorf("marshal Renew: %w", err) - } - resp, err := c.SendSoap(endpoint, string(body)) - if err != nil { - return err - } - _, err = readClose(resp) - return err -} - -func unsubscribePullPoint(c caller, endpoint string) error { - if endpoint == "" { - return nil - } - body, err := xml.Marshal(event.Unsubscribe{}) - if err != nil { - return fmt.Errorf("marshal Unsubscribe: %w", err) - } - resp, err := c.SendSoap(endpoint, string(body)) - if err != nil { - return err - } - _, err = readClose(resp) - return err -} - -func readClose(resp *http.Response) (string, error) { - if resp == nil || resp.Body == nil { - return "", errors.New("nil HTTP response") - } - defer resp.Body.Close() - // LimitReader prevents a hostile or buggy camera from OOMing the - // agent by streaming an unbounded response body. - b, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) - if err != nil { - return "", fmt.Errorf("read response body: %w", err) - } - return string(b), nil -} - -// unmarshalNode finds the first XML start element with the given local -// name and decodes it into out. ONVIF SOAP responses come wrapped in an -// envelope with multiple namespace prefixes; this helper sidesteps -// namespace matching by keying on local name only. -// -// When the camera returns a SOAP Fault instead of the expected -// response, the fault reason is surfaced as the error so callers can -// distinguish "auth failed" / "subscription expired" from "unparseable -// response". -func unmarshalNode(body, localName string, out any) error { - if reason := extractSOAPFault(body); reason != "" { - return fmt.Errorf("ONVIF SOAP fault: %s", reason) - } - dec := xml.NewDecoder(bytes.NewBufferString(body)) - for { - tok, err := dec.Token() - if err != nil { - if errors.Is(err, io.EOF) { - return fmt.Errorf("ONVIF response missing %s element", localName) - } - return fmt.Errorf("scan ONVIF response: %w", err) - } - start, ok := tok.(xml.StartElement) - if !ok { - continue - } - if start.Name.Local != localName { - continue - } - if err := dec.DecodeElement(out, &start); err != nil { - return fmt.Errorf("decode %s: %w", localName, err) - } - return nil - } -} - -var ( - // SOAP 1.1: reason - soap11FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?faultstring[^>]*>(.*?)\s]+:)?faultstring>`) - // SOAP 1.2: ...reason... - soap12FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Reason\b[^>]*>.*?<(?:[^:>\s]+:)?Text[^>]*>(.*?)\s]+:)?Text>`) -) - -// extractSOAPFault returns the human-readable reason text from a SOAP -// fault, or empty string when the body is not a fault. Handles both -// SOAP 1.1 (faultstring) and SOAP 1.2 (Reason/Text) shapes. -func extractSOAPFault(body string) string { - if !strings.Contains(body, "Fault") { - return "" - } - if m := soap11FaultRE.FindStringSubmatch(body); len(m) > 1 { - return strings.TrimSpace(m[1]) - } - if m := soap12FaultRE.FindStringSubmatch(body); len(m) > 1 { - return strings.TrimSpace(m[1]) - } - return "" -} - -// durationToXSD formats a Go time.Duration as an xsd:duration string in -// PTnS form. Second precision is sufficient — ONVIF cameras do not -// honour sub-second pull timeouts and intermediate routers may round in -// any case. -func durationToXSD(d time.Duration) string { - secs := int(d.Round(time.Second).Seconds()) - if secs <= 0 { - secs = 1 - } - return "PT" + strconv.Itoa(secs) + "S" -} diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index b7343fe..062cac7 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -351,3 +351,96 @@ func TestStream_DoesNotPanicOnPullExitingDuringClose(t *testing.T) { _ = s.Close() }) } + +// --- Close error / timeout paths ------------------------------------- + +func TestClose_ReturnsUnsubscribeError(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + fc.mu.Lock() + fc.defaultSendSoap = fakeResp{err: errors.New("simulated transport failure")} + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) + require.NoError(t, err) + + err = s.Close() + require.Error(t, err) + assert.Contains(t, err.Error(), "unsubscribe pull point") + assert.Contains(t, err.Error(), "simulated transport failure") +} + +func TestClose_BoundedByTimeoutOnHungUnsubscribe(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + block := make(chan struct{}) + defer close(block) // release the hung Unsubscribe so the fake's goroutine exits + fc.mu.Lock() + fc.blockUnsubscribe = block + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) + require.NoError(t, err) + + start := time.Now() + err = s.Close() + elapsed := time.Since(start) + require.Error(t, err) + assert.Contains(t, err.Error(), "timeout") + assert.Less(t, elapsed, closeUnsubscribeTimeout+time.Second, + "Close exceeded bound (%s); expected ~%s", elapsed, closeUnsubscribeTimeout) +} + +// --- NewStream edge cases -------------------------------------------- + +func TestNewStream_CtxAlreadyCancelled(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() // cancel before NewStream + + s, err := newStream(ctx, fc, Options{InitialTermination: 30 * time.Second}) + require.NoError(t, err) + require.NotNil(t, s) + + select { + case _, ok := <-s.Events(): + assert.False(t, ok, "events channel should be closed when ctx is pre-cancelled") + case <-time.After(time.Second): + t.Fatal("events channel was not closed within 1s") + } + _ = s.Close() +} + +// --- fakeCaller self-test -------------------------------------------- + +func TestFakeCaller_QueueThenDefaultFallback(t *testing.T) { + fc := newFakeCaller() + fc.queueSendSoap("first", nil) + fc.queueSendSoap("second", nil) + + r1, err := fc.SendSoap("ep", "body") + require.NoError(t, err) + b1 := make([]byte, 10) + n, _ := r1.Body.Read(b1) + assert.Equal(t, "first", string(b1[:n])) + + r2, _ := fc.SendSoap("ep", "body") + b2 := make([]byte, 10) + n, _ = r2.Body.Read(b2) + assert.Equal(t, "second", string(b2[:n])) + + // Queue is exhausted; default kicks in. + r3, err := fc.SendSoap("ep", "body") + require.NoError(t, err) + require.NotNil(t, r3) + b3 := make([]byte, 2048) + n, _ = r3.Body.Read(b3) + assert.Contains(t, string(b3[:n]), "PullMessagesResponse", + "default SendSoap should be an empty PullMessagesResponse envelope") +} diff --git a/event/stream/types.go b/event/stream/types.go index 9f1557d..a52f314 100644 --- a/event/stream/types.go +++ b/event/stream/types.go @@ -169,3 +169,41 @@ type Event struct { // first event whose Operation is not PropertyInitialized. AfterReconnect bool } + +// Op identifies which Stream operation failed. Used by ErrPullFailed, +// ErrRenewFailed and ErrRecreateFailed so consumers can branch with +// errors.As without parsing the wrapped message. +type Op string + +const ( + OpPull Op = "pull" + OpRenew Op = "renew" + OpRecreate Op = "recreate" +) + +// ErrPullFailed wraps a transient PullMessages failure. The pull loop +// surfaces it on the Errors channel and continues. Consumers can match +// with errors.As(err, &stream.ErrPullFailed{}). +type ErrPullFailed struct{ Err error } + +func (e ErrPullFailed) Error() string { return fmt.Sprintf("pull messages: %v", e.Err) } +func (e ErrPullFailed) Unwrap() error { return e.Err } +func (ErrPullFailed) Op() Op { return OpPull } + +// ErrRenewFailed wraps a Renew SOAP failure. Renew errors are usually +// recovered implicitly: the subscription dies, pull starts failing, +// and the reconnect logic recreates it. +type ErrRenewFailed struct{ Err error } + +func (e ErrRenewFailed) Error() string { return fmt.Sprintf("renew pull point: %v", e.Err) } +func (e ErrRenewFailed) Unwrap() error { return e.Err } +func (ErrRenewFailed) Op() Op { return OpRenew } + +// ErrRecreateFailed wraps a failed CreatePullPointSubscription during +// the reconnect path. The loop continues with exponential backoff; +// consumers seeing this repeatedly should consider the camera offline. +type ErrRecreateFailed struct{ Err error } + +func (e ErrRecreateFailed) Error() string { return fmt.Sprintf("recreate pull point: %v", e.Err) } +func (e ErrRecreateFailed) Unwrap() error { return e.Err } +func (ErrRecreateFailed) Op() Op { return OpRecreate } diff --git a/event/stream/types_test.go b/event/stream/types_test.go index ed2399d..8b295bb 100644 --- a/event/stream/types_test.go +++ b/event/stream/types_test.go @@ -1,6 +1,7 @@ package stream import ( + "errors" "testing" "time" @@ -114,3 +115,29 @@ func TestEventFieldAssignmentRoundTrip(t *testing.T) { assert.True(t, e.Timestamp.Equal(now)) assert.True(t, e.DeviceTime.Equal(deviceTime)) } + +// --- Typed errors ----------------------------------------------------- + +func TestTypedErrors_UnwrapAndOp(t *testing.T) { + inner := errors.New("boom") + tests := []struct { + name string + err error + op Op + }{ + {"pull", ErrPullFailed{Err: inner}, OpPull}, + {"renew", ErrRenewFailed{Err: inner}, OpRenew}, + {"recreate", ErrRecreateFailed{Err: inner}, OpRecreate}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.True(t, errors.Is(tc.err, inner), "errors.Is should unwrap to inner") + assert.Contains(t, tc.err.Error(), "boom") + if e, ok := tc.err.(interface{ Op() Op }); ok { + assert.Equal(t, tc.op, e.Op()) + } else { + t.Fatalf("%T does not expose Op()", tc.err) + } + }) + } +} From badcc8fba22cb0a2485e3099ebc267707cf6413d Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 15:19:15 +0200 Subject: [PATCH 44/53] docs(development): point readers at event/stream higher-level helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Development.md describes the wire-layer convention (one directory per Onvif Web Service, gen_commands.py for new SOAP command types) but does not mention that some directories also ship hand-written higher-level helpers built on top of those types. A new contributor reading the doc could reasonably assume event/ is purely auto-generated and miss event/stream. Adds a 'Higher-level helpers' section that calls out: * event/stream — the new channel-based event consumer. * event/topic — the existing topic identifier helpers. Also documents the placement convention (sub-package under the relevant web service directory) so future helpers land in a predictable spot. --- docs/Development.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/Development.md b/docs/Development.md index 4d5cdaf..74420fc 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -32,3 +32,23 @@ python3 python/gen_commands.py > **Note:** You can also typically run the generator within your IDE thanks to the `//go:generate` lines > towards the top of the `types.go` files. + +## Higher-level helpers + +Some web service directories ship hand-written, higher-level helpers +built on top of the wire-layer commands. These are normal Go packages +— **not** covered by the `gen_commands.py` workflow above and not +expected to be regenerated. + +- [event/stream](../event/stream) — channel-based event consumer that + owns the pull-point subscription lifecycle (Create, Pull, Renew, + Unsubscribe, reconnect with jittered backoff) and decodes + notifications into normalized typed Events. Vendor topic strings + (AXIS, Hikvision, Avigilon, Hanwha, Bosch, Dahua) are classified + into a small set of `Kind` values. See the package `doc.go` for the + public surface and usage. +- [event/topic](../event/topic) — topic identifier helpers. + +When adding a similar higher-level helper, place it under the relevant +web service directory as a sub-package so consumers find it next to +the wire-layer types it builds on. From fcc3a90f9bf3237dda4c1602db752deb34e1c6b3 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 18:43:08 +0200 Subject: [PATCH 45/53] docs(event/stream): trim comments to WHY, drop noise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit against the standard 'default to no comments; only add one when the WHY is non-obvious'. Net: 238 lines removed across 8 files, no behaviour change, tests still pass -race. What went --------- * Section banners (// ---------- Motion ----------): noise once per-rule citations exist. * Per-rule 'Data: IsMotion (xsd:boolean)' wire-format lines in topics.go: that's WHAT; the spec citation carries WHY. * Per-field doc on Event struct restating each field name (// Kind is the normalized event category) and the type-doc preamble. * Stringer doc comments ('// String implements fmt.Stringer.') and similar conventional-method noise. * 'Used by ErrPullFailed / ErrRenewFailed / ErrRecreateFailed' in the Op doc — the rule-named anti-pattern. * doc.go Invariants and Reconnect sections duplicating per-function docs. * Internal helper doc-comments restating what the function does (surfaceError, run, simpleItemsToMap first sentence, etc.). What stayed ----------- * Every spec / vendor-doc citation in topics.go. * Race-condition WHY in stream.go run() close ordering. * Workaround WHY in renew.go (absolute datetime vs duration). * WS-BaseNotification UTC rationale + vendor format list in decode.go. * Fleet-sizing and thundering-herd rationale in reconnect.go. * Stream consumer invariants (NewStream synchronous I/O, Errors non-blocking, Close idempotent + bounded). The change matches the codebase's stated style (CLAUDE.md): WHY only, no WHAT, no cross-file references, no current-task narration. --- event/stream/decode.go | 65 ++++++----------- event/stream/doc.go | 47 +++---------- event/stream/reconnect.go | 53 +++++--------- event/stream/renew.go | 26 +++---- event/stream/soap.go | 51 ++++++-------- event/stream/stream.go | 143 ++++++++++++++------------------------ event/stream/topics.go | 139 +++++++++++------------------------- event/stream/types.go | 112 +++++++++-------------------- 8 files changed, 199 insertions(+), 437 deletions(-) diff --git a/event/stream/decode.go b/event/stream/decode.go index ac3b88e..9a2a1fd 100644 --- a/event/stream/decode.go +++ b/event/stream/decode.go @@ -7,20 +7,9 @@ import ( "github.com/kerberos-io/onvif/event" ) -// decode converts a single ONVIF NotificationMessage into the package's -// normalized Event representation. Unexported because the only intended -// caller is the Stream; downstream consumers receive decoded Events on -// the Events channel. Tests reach decode directly because they're in -// the same package. -// -// deviceID is supplied by the caller because the message itself does not -// identify the originating camera. observedAt is recorded verbatim as -// Event.Timestamp; the camera-reported wsnt:UtcTime attribute (when -// present and parseable) populates Event.DeviceTime. -// -// When the Topic does not match any classifier rule the returned Event -// has Kind == KindUnknown but Source, Data and Topic are still populated -// so consumers can fall back to inspecting the wire form. +// decode converts a single ONVIF NotificationMessage into a normalized +// Event. Topic, Source and Data are always populated even when Kind is +// KindUnknown so consumers can fall back to the wire form. func decode(msg event.NotificationMessage, deviceID string, observedAt time.Time) Event { topic := string(msg.Topic.TopicKinds) desc := msg.Message.Message @@ -37,9 +26,8 @@ func decode(msg event.NotificationMessage, deviceID string, observedAt time.Time } } -// simpleItemsToMap collapses ONVIF SimpleItem lists to a Name->Value map. -// Returns nil for an empty list so empty notifications do not allocate -// and match the Event zero-value contract. +// simpleItemsToMap returns nil for an empty list so empty notifications +// do not allocate. func simpleItemsToMap(items []event.SimpleItem) map[string]string { if len(items) == 0 { return nil @@ -51,14 +39,9 @@ func simpleItemsToMap(items []event.SimpleItem) map[string]string { return m } -// extractState scans Data items for a boolean-like value and returns the -// first one as a State. Returns StateUnknown when no item parses — this -// is the correct outcome for edge-triggered topics such as +// extractState scans Data items for a boolean-like value, returning the +// first match. Returns StateUnknown for edge-triggered topics like // LineDetector/Crossed whose Data carries only an ObjectId. -// -// Iteration order over the original []SimpleItem is preserved so the -// behaviour stays deterministic per notification. (Map iteration is not -// involved; simpleItemsToMap is a separate path.) func extractState(items []event.SimpleItem) State { for _, it := range items { switch strings.ToLower(strings.TrimSpace(string(it.Value))) { @@ -71,9 +54,8 @@ func extractState(items []event.SimpleItem) State { return StateUnknown } -// parsePropertyOperation parses the wsnt:PropertyOperation attribute. -// The attribute is optional per WS-Notification; an empty or unrecognised -// value yields PropertyUnknown. +// parsePropertyOperation returns PropertyUnknown for absent (optional +// per WS-Notification) or unrecognised values. func parsePropertyOperation(s string) PropertyOperation { switch s { case "Initialized": @@ -87,15 +69,11 @@ func parsePropertyOperation(s string) PropertyOperation { } } -// parseDeviceTime parses the wsnt:UtcTime attribute, returning the zero -// time when the attribute is absent or unparseable. The result is -// normalised to UTC so equality comparisons across timezones work. -// -// xsd:dateTime in ONVIF messages is RFC 3339 in practice but real -// cameras emit several flavours: with/without sub-seconds, with colon -// or compact ("+0200") timezone offsets, and some older Hikvision -// firmwares omit the timezone entirely (treated as UTC per -// WS-BaseNotification which mandates UTC for UtcTime). +// parseDeviceTime parses wsnt:UtcTime, returning the zero time when +// absent or unparseable. Real cameras emit several flavours: with / +// without sub-seconds, colon or compact ("+0200") offsets, and some +// older Hikvision firmwares omit the timezone entirely (treated as +// UTC per WS-BaseNotification which mandates UTC for UtcTime). func parseDeviceTime(s string) time.Time { if s == "" { return time.Time{} @@ -108,14 +86,11 @@ func parseDeviceTime(s string) time.Time { return time.Time{} } -// deviceTimeLayouts lists the wsnt:UtcTime forms observed across vendor -// firmwares. Ordered from most-precise / most-common first so the -// happy path hits early. var deviceTimeLayouts = []string{ - time.RFC3339Nano, // 2026-05-21T10:30:00.500Z, ...+02:00 - time.RFC3339, // 2026-05-21T10:30:00Z, ...+02:00 - "2006-01-02T15:04:05.999-0700", // sub-second + compact offset (Geovision) - "2006-01-02T15:04:05-0700", // compact offset (some Dahua) - "2006-01-02T15:04:05.999", // no timezone, sub-second (rare) - "2006-01-02T15:04:05", // naked, no TZ (older Hikvision) + time.RFC3339Nano, + time.RFC3339, + "2006-01-02T15:04:05.999-0700", // Geovision + "2006-01-02T15:04:05-0700", // some Dahua + "2006-01-02T15:04:05.999", + "2006-01-02T15:04:05", // older Hikvision (no timezone) } diff --git a/event/stream/doc.go b/event/stream/doc.go index 8121525..ebd05aa 100644 --- a/event/stream/doc.go +++ b/event/stream/doc.go @@ -7,7 +7,7 @@ // // dev, _ := onvif.NewDevice(onvif.DeviceParams{Xaddr: "...", Username: "...", Password: "..."}) // s, err := stream.NewStream(ctx, dev, stream.Options{DeviceID: "front-door"}) -// if err != nil { /* construction failed: auth, network, or camera does not advertise events */ } +// if err != nil { /* construction failed: auth, network, or no event support */ } // defer s.Close() // // for ev := range s.Events() { @@ -17,43 +17,12 @@ // } // } // -// # Invariants +// NewStream performs network I/O so auth and reachability failures +// surface synchronously. Events and Errors close when the Stream stops; +// Errors sends are non-blocking so a stalled consumer drops older +// errors rather than blocking the pull loop. After a silent reconnect, +// the next batch's events carry Event.AfterReconnect=true. // -// NewStream performs network I/O. It returns once the -// CreatePullPointSubscription call has succeeded; auth and reachability -// failures surface as an error from NewStream rather than landing on -// the Errors channel later. -// -// Two goroutines back each Stream: a pull loop and a renew loop. Both -// exit when the context passed to NewStream is cancelled or when Close -// is called. Close is idempotent and bounded — see Stream.Close. -// -// Events is closed exactly when the Stream stops. Ranging over Events -// is safe; a closed channel terminates the loop without a Close call. -// Errors is also closed at stop time. Both channels are buffered (16 -// slots by default); sends to Errors are non-blocking so a stalled -// consumer drops older errors rather than the pull loop blocking on -// log output. -// -// The decoded Event preserves the wire form (Topic, raw Source and -// Data maps) so callers can fall back to inspecting non-standard -// payloads when Kind is KindUnknown. -// -// # Reconnect -// -// On ReconnectAfterFailures consecutive PullMessages failures the -// Stream silently recreates its pull-point subscription. ONVIF cameras -// replay each property's current value with PropertyInitialized on a -// new subscription; Events delivered between recreate and the first -// non-Initialized event carry Event.AfterReconnect=true so consumers -// can suppress duplicate handling. -// -// Set Options.DisableReconnect=true to opt out of recreate; the pull -// loop will retry against the original subscription until ctx cancel. -// -// # Topic classification -// -// Classify maps ONVIF topic strings to a small set of normalized Kind -// values across AXIS, Hikvision, Avigilon, Hanwha, Bosch and Dahua. See -// topics.go for the verified mapping table with public-doc citations. +// See topics.go for the verified topic→Kind mapping across AXIS, +// Hikvision, Avigilon, Hanwha, Bosch and Dahua. package stream diff --git a/event/stream/reconnect.go b/event/stream/reconnect.go index 52cb047..76bd791 100644 --- a/event/stream/reconnect.go +++ b/event/stream/reconnect.go @@ -6,31 +6,21 @@ import ( "time" ) -// maxRecreateBackoff caps exponential backoff between recreate attempts. -// Sized for fleet deployments: a 1000-camera setup recovering from a -// switch reboot would otherwise hammer the network with one recreate -// attempt per camera per 30s; 5 minutes gives the network time to -// settle while still recovering promptly when a single camera comes -// back. +// maxRecreateBackoff caps exponential backoff between recreate +// attempts. Sized for fleet deployments: at 30s a 1000-camera setup +// recovering from a switch reboot would generate sustained +// reconnect traffic; 5 minutes lets the network settle. const maxRecreateBackoff = 5 * time.Minute -// jitterFraction is the symmetric jitter applied to recreate backoff: -// the actual sleep is sampled from [backoff*(1-jitter), backoff*(1+jitter)]. -// Prevents thundering-herd reconnects when many cameras drop together -// (switch reboot, NAT timeout). +// jitterFraction prevents thundering-herd reconnects when many +// cameras drop together (switch reboot, NAT timeout). const jitterFraction = 0.25 -// pullLoop is the main pull goroutine of a Stream. It calls -// PullMessages in a tight loop, decodes results into Events and feeds -// the Events channel. -// -// After ReconnectAfterFailures consecutive pull errors it asks -// attemptRecreate to recreate the pull-point subscription, marking the -// next batch's events with AfterReconnect so consumers can suppress -// duplicate handling of the ONVIF Initialized-replay that follows a -// new subscription. -// -// Exits when ctx is cancelled. +// pullLoop runs PullMessages → decode → Events. After +// ReconnectAfterFailures consecutive errors it asks attemptRecreate +// to rebuild the subscription. The next batch's events carry +// AfterReconnect=true so consumers can suppress the Initialized +// replay ONVIF emits on a new subscription. func (s *Stream) pullLoop(ctx context.Context) { var failures int recreateBackoff := s.opts.RetryBackoff @@ -59,7 +49,6 @@ func (s *Stream) pullLoop(ctx context.Context) { } continue } - // Successful pull resets failure tracking. failures = 0 recreateBackoff = s.opts.RetryBackoff observedAt := s.now() @@ -67,11 +56,8 @@ func (s *Stream) pullLoop(ctx context.Context) { ev := decode(m, s.opts.DeviceID, observedAt) if afterReconnect { ev.AfterReconnect = true - // ONVIF replays current state with - // PropertyInitialized on a new subscription. - // Clear the flag as soon as we see anything - // other than Initialized — at that point we - // have transitioned to live events. + // Clear once the camera transitions past the + // Initialized replay to live events. if ev.Operation != PropertyInitialized { afterReconnect = false } @@ -85,11 +71,8 @@ func (s *Stream) pullLoop(ctx context.Context) { } } -// attemptRecreate calls CreatePullPointSubscription and on success -// installs the new endpoint atomically. The first return is true when -// recreate succeeded just now (caller flags the next batch with -// AfterReconnect). The second return is false only if ctx was cancelled -// during backoff (caller should exit the run loop). +// attemptRecreate returns (justRecreated, cont). cont is false only +// when ctx cancelled during backoff so the caller exits the loop. func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) (justRecreated, cont bool) { addr, err := createPullPoint(s.caller, s.opts) if err != nil { @@ -109,10 +92,8 @@ func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *ti return true, true } -// jitter returns d perturbed by ±jitterFraction. Used to spread -// recreate attempts across a fleet so a synchronised drop (switch -// reboot, DHCP storm) does not cause a synchronised reconnect surge. -// Returns at least 1ns to keep sleepCtx happy. +// jitter perturbs d by ±jitterFraction so synchronised drops do not +// produce a synchronised reconnect surge. func jitter(d time.Duration) time.Duration { if d <= 0 { return time.Nanosecond diff --git a/event/stream/renew.go b/event/stream/renew.go index 6a8763a..cd3f199 100644 --- a/event/stream/renew.go +++ b/event/stream/renew.go @@ -10,18 +10,15 @@ import ( "github.com/kerberos-io/onvif/xsd" ) -// renewLoop refreshes the subscription before InitialTermination expires. -// Exits when ctx is cancelled. Renew failures surface as ErrRenewFailed -// on the Errors channel; the loop continues because a permanently -// failing renew will eventually drop the subscription and the pull -// loop's reconnect path will recover (recreate is the only reliable -// recovery once a subscription is GC'd at the camera). +// renewLoop surfaces renew failures and continues. A permanently +// failing renew lets the subscription die at the camera; the pull +// loop's reconnect path then recreates it — recreate is the only +// reliable recovery once a subscription is GC'd. func (s *Stream) renewLoop(ctx context.Context) { interval := s.opts.InitialTermination - s.opts.RenewMargin if interval <= 0 { - // Pathological config (margin >= termination): fall back to - // renewing at half the termination so we still refresh, - // rather than busy-looping or never renewing. + // Pathological config (margin >= termination): renew at + // half termination so we still refresh. interval = s.opts.InitialTermination / 2 if interval <= 0 { interval = time.Second @@ -41,13 +38,10 @@ func (s *Stream) renewLoop(ctx context.Context) { } } -// renewPullPoint issues a wsnt:Renew SOAP against the given -// subscription endpoint with an absolute TerminationTime. -// -// WS-BaseNotification §6.1.1 declares TerminationTime as xsd:dateTime -// OR xsd:duration, but older Hikvision, some Dahua and some Bosch -// firmwares reject the relative-duration form. We send an absolute -// UTC datetime to match what production NVRs do. +// renewPullPoint sends Renew with an absolute UTC TerminationTime. +// WS-BaseNotification §6.1.1 also allows xsd:duration but older +// Hikvision, some Dahua and some Bosch firmwares reject the +// relative form. func renewPullPoint(c caller, endpoint string, opts Options) error { absoluteEnd := time.Now().UTC().Add(opts.InitialTermination).Format("2006-01-02T15:04:05Z") req := event.Renew{TerminationTime: xsd.String(absoluteEnd)} diff --git a/event/stream/soap.go b/event/stream/soap.go index 4d9bcf6..879b22c 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -16,16 +16,12 @@ import ( "github.com/kerberos-io/onvif/xsd" ) -// maxResponseBytes caps the size of a SOAP response we will buffer in -// memory. ONVIF PullMessages bodies are normally <100KB even with dense -// analytics payloads; 10 MiB is comfortably above legitimate traffic -// while keeping a hostile or buggy camera from OOMing the process. +// maxResponseBytes caps SOAP response buffering. ONVIF PullMessages +// bodies are normally <100KB even with dense analytics payloads; +// 10 MiB is comfortably above legitimate traffic while keeping a +// hostile or buggy camera from OOMing the process. const maxResponseBytes = 10 << 20 -// createPullPoint issues a CreatePullPointSubscription against the -// device service. Returns the SubscriptionReference Address, which is -// the endpoint subsequent PullMessages / Renew / Unsubscribe calls -// target. func createPullPoint(c caller, opts Options) (string, error) { term := xsd.String(durationToXSD(opts.InitialTermination)) req := event.CreatePullPointSubscription{InitialTerminationTime: &term} @@ -56,9 +52,8 @@ func createPullPoint(c caller, opts Options) (string, error) { return addr, nil } -// pullMessages issues PullMessages against an active subscription -// endpoint and returns the decoded NotificationMessage list. Empty -// slice (not error) when the camera had nothing within PullTimeout. +// pullMessages returns an empty slice (no error) when the camera had +// nothing within PullTimeout. func pullMessages(c caller, endpoint string, opts Options) ([]event.NotificationMessage, error) { req := event.PullMessages{ Timeout: xsd.Duration(durationToXSD(opts.PullTimeout)), @@ -83,9 +78,8 @@ func pullMessages(c caller, endpoint string, opts Options) ([]event.Notification return decoded.NotificationMessage, nil } -// unsubscribePullPoint sends a best-effort Unsubscribe to release the -// subscription server-side. Empty endpoint is a no-op (the construction -// failed before installing one). +// unsubscribePullPoint is best-effort. Empty endpoint is a no-op +// (construction failed before installing one). func unsubscribePullPoint(c caller, endpoint string) error { if endpoint == "" { return nil @@ -102,9 +96,6 @@ func unsubscribePullPoint(c caller, endpoint string) error { return err } -// readClose reads at most maxResponseBytes from resp.Body and closes -// it. LimitReader prevents a hostile or buggy camera from OOMing the -// agent by streaming an unbounded response. func readClose(resp *http.Response) (string, error) { if resp == nil || resp.Body == nil { return "", errors.New("nil HTTP response") @@ -118,14 +109,13 @@ func readClose(resp *http.Response) (string, error) { } // unmarshalNode finds the first XML start element with the given local -// name and decodes it into out. ONVIF SOAP responses come wrapped in an -// envelope with multiple namespace prefixes; this helper sidesteps -// namespace matching by keying on local name only. +// name and decodes it into out. ONVIF SOAP responses are wrapped in an +// envelope with many namespace prefixes; keying on local name only +// sidesteps namespace matching. // -// When the camera returns a SOAP Fault instead of the expected -// response, the fault reason is surfaced as the error so callers can -// distinguish "auth failed" / "subscription expired" from "unparseable -// response". +// When the camera returns a SOAP Fault, the fault reason is returned +// as the error so callers can distinguish auth / expired-subscription +// from "unparseable response". func unmarshalNode(body, localName string, out any) error { if reason := extractSOAPFault(body); reason != "" { return fmt.Errorf("ONVIF SOAP fault: %s", reason) @@ -160,9 +150,9 @@ var ( soap12FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Reason\b[^>]*>.*?<(?:[^:>\s]+:)?Text[^>]*>(.*?)\s]+:)?Text>`) ) -// extractSOAPFault returns the human-readable reason text from a SOAP -// fault, or empty string when the body is not a fault. Handles both -// SOAP 1.1 (faultstring) and SOAP 1.2 (Reason/Text) shapes. +// extractSOAPFault returns the reason text from a SOAP fault or empty +// when the body is not a fault. Handles SOAP 1.1 (faultstring) and +// SOAP 1.2 (Reason/Text) shapes. func extractSOAPFault(body string) string { if !strings.Contains(body, "Fault") { return "" @@ -176,10 +166,9 @@ func extractSOAPFault(body string) string { return "" } -// durationToXSD formats a Go time.Duration as an xsd:duration string in -// PTnS form. Second precision is sufficient — ONVIF cameras do not -// honour sub-second pull timeouts and intermediate routers may round in -// any case. +// durationToXSD formats a duration as xsd:duration PTnS. Second +// precision is sufficient — ONVIF cameras do not honour sub-second +// pull timeouts. func durationToXSD(d time.Duration) string { secs := int(d.Round(time.Second).Seconds()) if secs <= 0 { diff --git a/event/stream/stream.go b/event/stream/stream.go index 4f11415..e548d3c 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -10,70 +10,51 @@ import ( "github.com/kerberos-io/onvif" ) -// closeUnsubscribeTimeout bounds the SOAP Unsubscribe call issued by -// Close so a hung camera connection cannot wedge the caller. The -// subscription expires at the camera anyway once InitialTermination -// elapses, so a missed unsubscribe is at worst cosmetic. +// closeUnsubscribeTimeout bounds the Unsubscribe SOAP call issued by +// Close. A subscription expires at the camera once InitialTermination +// elapses without a renew, so a missed unsubscribe is at worst +// cosmetic. const closeUnsubscribeTimeout = 5 * time.Second // Options configures a Stream. // -// Zero-value policy: every duration / int field treats zero as "use the -// default". To opt out of reconnect entirely set DisableReconnect=true -// (sentinel `ReconnectAfterFailures=0` would otherwise collide with the -// default-injection policy). To get a synchronous (unbuffered) channel -// pair set BufferSize=-1. +// Zero-value policy: every duration / int field treats zero as "use +// the default". To opt out of reconnect set DisableReconnect=true +// (ReconnectAfterFailures=0 would otherwise collide with the default +// injection). For unbuffered Events / Errors channels set +// BufferSize=-1. type Options struct { - // DeviceID identifies the camera in emitted Events. Recommended so - // a single channel can fan in multiple cameras. Empty is allowed. DeviceID string - // RawTopicFilter is the raw ONVIF ConcreteSet TopicExpression - // filter passed to CreatePullPointSubscription. Empty means no - // filter — required for AXIS, accepted by every other vendor we - // support. The name carries 'Raw' because the value is fed verbatim - // into the SOAP envelope: callers should normally leave it empty - // and rely on Classify for routing rather than ask the camera to - // filter server-side, which is fragile across vendors. + // RawTopicFilter is the ONVIF ConcreteSet TopicExpression filter + // passed verbatim to CreatePullPointSubscription. Callers should + // normally leave this empty and rely on Classify for routing — + // server-side filtering is fragile across vendors and empty is + // required for AXIS. RawTopicFilter string - // PullTimeout is the server-side wait time in each PullMessages - // call (xsd:duration). The camera returns early when messages are - // available; otherwise it returns empty after this timeout. Zero - // means default (5s). + // PullTimeout — zero means default (5s). PullTimeout time.Duration - // MessageLimit caps the number of NotificationMessage entries - // returned per PullMessages call. Zero means default (32). A busy - // AXIS with many configured inputs can burst beyond 10 per pull; - // 32 covers that without significantly enlarging quiet pulls. + // MessageLimit — zero means default (32). Busy AXIS cameras with + // many configured rules can burst beyond 10 per pull. MessageLimit int - // InitialTermination is the requested subscription lifetime passed - // to CreatePullPointSubscription. The renew loop refreshes well - // before this expires. Zero means default (60s). + // InitialTermination — zero means default (60s). InitialTermination time.Duration - // RenewMargin is how long before InitialTermination expiry the - // renew loop fires. Larger margins tolerate slower networks at the - // cost of more renew SOAP calls. Zero means default (10s). + // RenewMargin — larger margins tolerate slower networks at the + // cost of more renew calls. Zero means default (10s). RenewMargin time.Duration - // ReconnectAfterFailures is the consecutive PullMessages failure - // count that triggers a CreatePullPointSubscription recreate. The - // camera or pull-point can die for many reasons (camera reboot, - // subscription garbage-collected after a renew miss, intermediate - // NAT timeout); rebuilding the subscription is the only reliable - // recovery. Zero means default (3). To disable reconnect entirely - // set DisableReconnect=true. + // ReconnectAfterFailures — pull-points die for many reasons + // (camera reboot, subscription GC after a renew miss, NAT + // timeout); rebuilding the subscription is the only reliable + // recovery. Zero means default (3). Set DisableReconnect=true + // to disable. ReconnectAfterFailures int - // DisableReconnect skips automatic CreatePullPointSubscription - // recreate. The pull loop will continue retrying against the - // original endpoint until ctx is cancelled. Useful for tests or - // callers managing recovery externally. + // DisableReconnect makes the pull loop retry against the + // original endpoint until ctx is cancelled. DisableReconnect bool - // RetryBackoff is the initial sleep between a pull/recreate failure - // and the next attempt. Recreate failures double this up to a 30s - // ceiling. Zero means default (1s). + // RetryBackoff is the base sleep between pull/recreate failures. + // Recreate failures double this up to maxRecreateBackoff. Zero + // means default (1s). RetryBackoff time.Duration - // BufferSize is the buffer size of the Events and Errors channels. - // Larger buffers absorb consumer hiccups at the cost of memory. - // Zero means default (16); use -1 for unbuffered (synchronous) - // channels. + // BufferSize — zero means default (16); use -1 for unbuffered. BufferSize int } @@ -109,7 +90,6 @@ func (o Options) withDefaults() Options { if o.RetryBackoff > 0 { d.RetryBackoff = o.RetryBackoff } - // BufferSize: zero -> default; negative -> 0 (unbuffered). switch { case o.BufferSize > 0: d.BufferSize = o.BufferSize @@ -122,13 +102,9 @@ func (o Options) withDefaults() Options { return d } -// caller is the subset of *onvif.Device the Stream depends on. Tests -// substitute a fake; production code uses the device adapter. -// -// Implementations must be safe for concurrent use: the pull loop and -// renew loop call into caller from separate goroutines. *onvif.Device -// satisfies this because its HTTP client is the goroutine-safe -// http.Client. +// caller is the *onvif.Device subset Stream depends on. Implementations +// must be safe for concurrent use — pull and renew goroutines call in +// from separate goroutines. *onvif.Device satisfies this via http.Client. type caller interface { CallMethod(method any) (*http.Response, error) SendSoap(endpoint, body string) (*http.Response, error) @@ -144,12 +120,9 @@ func (d deviceCaller) SendSoap(endpoint, body string) (*http.Response, error) { return d.dev.SendSoap(endpoint, body) } -// Stream owns a single ONVIF pull-point subscription and surfaces the -// decoded notifications on a typed channel. Close stops the background -// goroutine and unsubscribes from the camera. -// -// A Stream is safe for concurrent use by Close from any goroutine while -// readers consume Events / Errors; Close is idempotent. +// Stream owns a single ONVIF pull-point subscription. Safe for Close +// from any goroutine while readers consume Events / Errors. Close is +// idempotent. type Stream struct { caller caller opts Options @@ -166,7 +139,7 @@ type Stream struct { closeOnce sync.Once closeErr error - // now is overridable in tests to make timestamps deterministic. + // now is overridable so tests can make timestamps deterministic. now func() time.Time } @@ -182,14 +155,11 @@ func (s *Stream) setPullPoint(addr string) { s.pullPoint = addr } -// NewStream creates a Stream against an ONVIF device. It performs the -// CreatePullPointSubscription call synchronously so connectivity and -// authentication problems surface immediately as an error rather than -// landing on the Errors channel later. The background pull loop starts -// before NewStream returns. +// NewStream creates a Stream and performs CreatePullPointSubscription +// synchronously so connectivity and authentication failures surface +// from NewStream rather than landing on Errors later. // -// The returned Stream stops when ctx is cancelled or when Close is -// called. +// The returned Stream stops when ctx is cancelled or Close is called. func NewStream(ctx context.Context, dev *onvif.Device, opts Options) (*Stream, error) { return newStream(ctx, deviceCaller{dev: dev}, opts) } @@ -215,22 +185,20 @@ func newStream(ctx context.Context, c caller, opts Options) (*Stream, error) { return s, nil } -// Events returns the channel of decoded notifications. The channel is -// closed when the Stream stops. +// Events returns the channel of decoded notifications. Closed when +// the Stream stops. func (s *Stream) Events() <-chan Event { return s.events } -// Errors returns the channel of non-fatal errors encountered while -// pulling. Sends are non-blocking, so consumers that fall behind drop -// older errors. The channel is closed when the Stream stops. +// Errors returns the channel of non-fatal errors. Sends are +// non-blocking; consumers that fall behind drop older errors. Closed +// when the Stream stops. func (s *Stream) Errors() <-chan error { return s.errors } -// Close stops the background goroutine, waits for it to exit, and -// unsubscribes from the camera. Subsequent calls are no-ops. +// Close stops the background goroutines, waits for them to exit and +// Unsubscribes from the camera. Subsequent calls are no-ops. // // Unsubscribe is bounded by closeUnsubscribeTimeout so a hung camera -// connection cannot wedge the caller. On timeout Close still returns -// promptly; the subscription will expire at the camera once -// InitialTermination + RenewMargin elapses without a renew. +// connection cannot wedge the caller. func (s *Stream) Close() error { s.closeOnce.Do(func() { s.cancel() @@ -252,8 +220,6 @@ func (s *Stream) Close() error { return s.closeErr } -// run orchestrates the pull and renew goroutines and closes the -// emission channels once both have exited. func (s *Stream) run(ctx context.Context) { var wg sync.WaitGroup wg.Add(1) @@ -265,15 +231,13 @@ func (s *Stream) run(ctx context.Context) { wg.Wait() // Explicit close order after both goroutines have exited so a - // future maintainer extending this function does not accidentally - // rely on defer-ordering for channel-close safety. + // future maintainer extending this function does not rely on + // defer-ordering for channel-close safety. close(s.errors) close(s.events) close(s.done) } -// surfaceError sends err on the errors channel non-blockingly so a -// stalled consumer cannot block the pull or renew loop. func (s *Stream) surfaceError(err error) { select { case s.errors <- err: @@ -281,8 +245,7 @@ func (s *Stream) surfaceError(err error) { } } -// sleepCtx blocks for d or until ctx is cancelled. Returns true if d -// elapsed, false if ctx was cancelled. +// sleepCtx returns false if ctx was cancelled, true if d elapsed. func sleepCtx(ctx context.Context, d time.Duration) bool { t := time.NewTimer(d) defer t.Stop() diff --git a/event/stream/topics.go b/event/stream/topics.go index 469027b..9071dad 100644 --- a/event/stream/topics.go +++ b/event/stream/topics.go @@ -2,24 +2,23 @@ package stream import "strings" -// Classify maps an ONVIF topic string (e.g. "tns1:VideoSource/MotionAlarm") -// to the normalized Kind that callers should switch on. Returns +// Classify maps an ONVIF topic string to the normalized Kind. Returns // KindUnknown when no rule matches. // -// The classifier strips XML-namespace prefixes (tns1:, tnsaxis:, -// tnssamsung:, ...) from each "/"-separated segment of the topic so it is -// robust to vendor namespace variants. Matching is case-sensitive because -// ONVIF topic identifiers are case-sensitive per the spec. +// The classifier strips XML-namespace prefixes from each "/"-separated +// segment so it is robust to vendor namespaces (tns1:, tnsaxis:, +// tnssamsung:, ...). Matching is case-sensitive — ONVIF topics are +// case-sensitive per the spec. // // Sources cross-checked when building the rule set below: // - ONVIF Topic Namespace XML // https://www.onvif.org/onvif/ver10/topics/topicns.xml -// - ONVIF Analytics Service Spec (RuleEngine topics) +// - ONVIF Analytics Service Spec // https://www.onvif.org/specs/srv/analytics/ONVIF-VideoAnalytics-Service-Spec-v220.pdf -// - ONVIF Device IO Service Spec (DigitalInput, Relay) +// - ONVIF Device IO Service Spec // https://www.onvif.org/specs/srv/io/ONVIF-DeviceIo-Service-Spec.pdf // - openvideolibs/onvif-parsers (Apache-2.0) — empirical topic table -// extracted from Home Assistant ONVIF integration +// extracted from Home Assistant // https://github.com/openvideolibs/onvif-parsers func Classify(topic string) Kind { if topic == "" { @@ -34,15 +33,10 @@ func Classify(topic string) Kind { return KindUnknown } -// canonicalizeTopic strips the XML-namespace prefix (anything up to and -// including the first ':') from each "/"-separated segment. This collapses -// vendor variants like "tns1:Device/tns1:Trigger/tns1:Relay" (Avigilon -// serialisation) and "tns1:Device/Trigger/Relay" (everyone else) to a -// single matchable form. -// -// A segment that is only a prefix (e.g. "tns1:") canonicalizes to the -// empty string. Multiple colons in one segment are not expected in real -// ONVIF topics; the first colon wins. +// canonicalizeTopic strips the XML-namespace prefix from each +// "/"-separated segment, collapsing Avigilon's per-segment-prefixed +// form ("tns1:Device/tns1:Trigger/tns1:Relay") and the plain form +// ("tns1:Device/Trigger/Relay") to the same matchable path. func canonicalizeTopic(topic string) string { segments := strings.Split(topic, "/") for i, seg := range segments { @@ -53,133 +47,88 @@ func canonicalizeTopic(topic string) string { return strings.Join(segments, "/") } -// topicRules is evaluated in order; first match wins. Keep more specific -// rules ahead of broader ones — e.g. "ObjectAnalytics/" must precede any -// future bare "Analytics" rule, and "MyRuleDetector/HumanDetect" must -// precede a hypothetical broader "MyRuleDetector" entry. Each rule cites -// the documentation that supports including it. -// -// Substring matching is intentional so vendor-specific path prefixes -// outside the standard tns1: namespace (e.g. -// tnsaxis:CameraApplicationPlatform/...) still match. -// -// Note on edge-triggered topics: tns1:RuleEngine/LineDetector/Crossed -// carries an ObjectId rather than a State boolean. Consumers of Crossed -// must not expect a level-triggered Active/Inactive semantic — the Stream -// decoder will leave State as StateUnknown for these. +// topicRules is evaluated in order — first match wins. Keep more +// specific rules ahead of broader ones. LineDetector/Crossed is +// edge-triggered (no boolean State); the decoder leaves State as +// StateUnknown for it. var topicRules = []struct { needle string kind Kind }{ - // ---------- Motion ------------------------------------------------- - - // tns1:VideoSource/MotionAlarm — Profile S basic motion. Emitted by - // AXIS (basic VMD), Bosch, Dahua, Hikvision (newer firmware) and - // Hanwha as a fallback. Data SimpleItem: State (xsd:boolean). + // tns1:VideoSource/MotionAlarm — Profile S basic motion. // https://www.onvif.org/ver10/topics/topicns.xml // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"VideoSource/MotionAlarm", KindMotion}, // tns1:VideoAnalytics/MotionAlarm — Bosch publishes motion under - // VideoAnalytics rather than VideoSource. Data: State. + // VideoAnalytics rather than VideoSource. // https://media.boschsecurity.com/fs/media/pb/media/partners_1/integration_tools_1/developer/bosch-metadata-and-iva-events.pdf {"VideoAnalytics/MotionAlarm", KindMotion}, - // tns1:VideoAnalytics/tnssamsung:MotionDetection — Hanwha/Samsung - // Wisenet vendor-namespaced motion. Data: Motion ("0"/"1"). + // tns1:VideoAnalytics/tnssamsung:MotionDetection — Hanwha vendor. // https://github.com/home-assistant/core/issues/66493 {"VideoAnalytics/MotionDetection", KindMotion}, // tns1:RuleEngine/CellMotionDetector/Motion — ONVIF Analytics - // standard cell-motion rule. Emitted by AXIS (VMD3+), Hikvision, - // Avigilon analytics, others. Data: IsMotion (xsd:boolean). + // standard cell-motion rule. // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.3 // https://www.hikvisioneurope.com/eu/portal/portal/Technical%20Materials/24%20How%20To/CCTV/How%20to%20solve%20third%20party%20camera%20motion%20detection%20issue.pdf {"CellMotionDetector/Motion", KindMotion}, - // tns1:RuleEngine/MotionRegionDetector/Motion — AXIS-specific region - // motion rule. Data: IsMotion (xsd:boolean). + // tns1:RuleEngine/MotionRegionDetector/Motion — AXIS region rule. // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"MotionRegionDetector/Motion", KindMotion}, - // AXIS Guard suite — vendor analytics apps that fire motion-like - // events with CameraProfile suffixes. Treated as motion so - // they can drive motion-triggered recording on cameras configured - // with these apps instead of basic VMD. + // AXIS Guard suite — vendor analytics apps with CameraProfile + // suffixes. Treated as motion so they can drive motion-triggered + // recording on cameras using these apps instead of basic VMD. // https://developer.axis.com/vapix/applications/motion-guard {"CameraApplicationPlatform/MotionGuard/", KindMotion}, {"CameraApplicationPlatform/FenceGuard/", KindMotion}, {"CameraApplicationPlatform/LoiteringGuard/", KindMotion}, - // ---------- Tampering --------------------------------------------- - - // tns1:RuleEngine/TamperDetector/Tamper — standard ONVIF tamper - // rule. Data: IsTamper (xsd:boolean). Anchored on the rule-name - // segment so "TamperDetectorLog" (hypothetical) does not match. + // tns1:RuleEngine/TamperDetector/Tamper — standard ONVIF tamper rule. // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.5 {"TamperDetector/Tamper", KindTampering}, - // tns1:VideoSource/GlobalSceneChange/ImagingService — Hikvision (and - // others) emit this on real lens-cover / scene substitution. This is - // the proper tamper signal on firmwares without TamperDetector. + // tns1:VideoSource/GlobalSceneChange/ImagingService — the proper + // lens-cover signal on firmwares without TamperDetector. // https://www.onvif.org/ver10/topics/topicns.xml {"GlobalSceneChange", KindTampering}, - // tns1:VideoAnalytics/tnssamsung:TamperingDetection — Hanwha vendor. + // tns1:VideoAnalytics/tnssamsung:TamperingDetection — Hanwha. // https://github.com/home-assistant/core/issues/66493 {"VideoAnalytics/TamperingDetection", KindTampering}, - // ---------- Image quality ----------------------------------------- - - // tns1:VideoSource/ImageTooDark|ImageTooBright|ImageTooBlurry — - // imaging-quality alarms. Integrators (Milestone, Genetec, Frigate) - // route these separately from tamper because they fire on legitimate - // sunset/dawn/condensation transitions, not on actual interference. + // VideoSource/ImageToo* — imaging-quality alarms. See KindImageQuality + // for the rationale on splitting these out from KindTampering. // https://www.onvif.org/ver10/topics/topicns.xml {"VideoSource/ImageTooDark", KindImageQuality}, {"VideoSource/ImageTooBright", KindImageQuality}, {"VideoSource/ImageTooBlurry", KindImageQuality}, - // ---------- Digital I/O ------------------------------------------- - - // tns1:Device/Trigger/DigitalInput — standard ONVIF DeviceIO topic. - // Avigilon emits the per-segment-prefixed variant - // "tns1:Device/tns1:Trigger/tns1:DigitalInput"; canonicalization - // folds both to the same path. Data: LogicalState (xsd:boolean), - // Source: InputToken. + // tns1:Device/Trigger/DigitalInput — standard. Avigilon's per-segment- + // prefixed serialisation ("tns1:Device/tns1:Trigger/tns1:DigitalInput") + // folds to the same canonical path. // ONVIF-DeviceIo-Service-Spec.pdf §5.2 {"Trigger/DigitalInput", KindDigitalInput}, - - // tns1:Device/Trigger/Relay — standard ONVIF DeviceIO topic. Same - // canonicalisation note as DigitalInput. Data: LogicalState, - // Source: RelayToken. // ONVIF-DeviceIo-Service-Spec.pdf §5.3 {"Trigger/Relay", KindDigitalOutput}, - // ---------- Object analytics -------------------------------------- - // tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1Scenario - // — AXIS Object Analytics. Scenario suffixes are numeric per the - // AOA configuration (Device1Scenario1, Device1Scenario2, ...). Data: - // active ("0"/"1") plus classType / confidence when configured. + // — Scenario suffixes are numeric per AOA configuration. Prefix-match + // because of the dynamic suffix. // https://developer.axis.com/analytics/axis-object-analytics/how-to-guides/axis-object-analytics-counting-data/ {"ObjectAnalytics/", KindObjectDetected}, - // tns1:RuleEngine/LineDetector/Crossed — line crossing (Hikvision, - // Bosch IVA, others). Data: ObjectId (xsd:int); edge-triggered, no - // State boolean. - // https://www.onvif.org/specs/srv/analytics/ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.4 + // ONVIF-VideoAnalytics-Service-Spec-v220.pdf §5.4 {"LineDetector/Crossed", KindObjectDetected}, - - // tns1:RuleEngine/FieldDetector/ObjectsInside — intrusion / region - // detector (Hikvision, Bosch, Dahua). Data: IsInside (xsd:boolean). {"FieldDetector/ObjectsInside", KindObjectDetected}, - // tns1:RuleEngine/MyRuleDetector/ — vendor-defined rule - // names under the ONVIF MyRuleDetector container. We whitelist - // object-class rules emitted by Bosch IVA, Dahua SMD and Hikvision - // AcuSense so non-object rules under the same container (Bosch - // Counter, Occupancy) do not get mis-classified. + // tns1:RuleEngine/MyRuleDetector/ — vendor rules under the + // ONVIF MyRuleDetector container. Explicitly whitelisted because the + // same container also carries non-object rules (Bosch Counter, + // Occupancy) that must not classify as ObjectDetected. // https://media.boschsecurity.com/fs/media/pb/media/partners_1/integration_tools_1/developer/bosch-metadata-and-iva-events.pdf {"MyRuleDetector/HumanDetect", KindObjectDetected}, {"MyRuleDetector/VehicleDetect", KindObjectDetected}, @@ -187,16 +136,8 @@ var topicRules = []struct { {"MyRuleDetector/ObjectsInside", KindObjectDetected}, {"MyRuleDetector/FaceDetect", KindObjectDetected}, - // ---------- Audio -------------------------------------------------- - - // tns1:AudioAnalytics/Audio/DetectedSound — standard ONVIF audio - // detection. Data: State (xsd:boolean). {"Audio/DetectedSound", KindAudioAlarm}, - - // tns1:AudioSource/tnsaxis:TriggerLevel — AXIS audio level alarm. // https://developer.axis.com/vapix/network-video/event-and-action-services/ {"AudioSource/TriggerLevel", KindAudioAlarm}, - - // tns1:AudioAnalytics/tnssamsung:SoundDetection — Hanwha vendor. {"AudioAnalytics/SoundDetection", KindAudioAlarm}, } diff --git a/event/stream/types.go b/event/stream/types.go index a52f314..6e84419 100644 --- a/event/stream/types.go +++ b/event/stream/types.go @@ -10,32 +10,19 @@ import ( type Kind uint8 const ( - // KindUnknown is the zero value; used when a topic does not match any - // known classification. KindUnknown Kind = iota - // KindMotion covers motion detection from any vendor (e.g. AXIS - // VideoSource/MotionAlarm, Hikvision RuleEngine/CellMotionDetector). KindMotion - // KindTampering covers true tamper alarms (lens cover, scene - // substitution). Imaging-quality alarms map to KindImageQuality. KindTampering - // KindImageQuality covers VideoSource imaging alarms such as - // ImageTooDark, ImageTooBright and ImageTooBlurry. Most integrators - // treat these separately from tamper because they fire on legitimate - // sunset/dawn/condensation transitions. + // KindImageQuality covers VideoSource imaging alarms. Kept separate + // from KindTampering because they fire on legitimate sunset / dawn / + // condensation transitions, not on interference. KindImageQuality - // KindDigitalInput covers external sensor inputs wired to the camera. KindDigitalInput - // KindDigitalOutput covers relay output state changes on the camera. KindDigitalOutput - // KindObjectDetected covers analytics-based object/person/vehicle - // detection events. KindObjectDetected - // KindAudioAlarm covers audio-level / loud-noise alarms. KindAudioAlarm ) -// String implements fmt.Stringer. func (k Kind) String() string { switch k { case KindUnknown: @@ -60,9 +47,8 @@ func (k Kind) String() string { } // State is the active/inactive level carried by a boolean ONVIF property -// event (e.g. IsMotion=true/false). StateUnknown is used both when the -// value cannot be parsed and when the topic is edge-triggered and carries -// no boolean state (e.g. LineDetector/Crossed). +// event. StateUnknown is used both when the value cannot be parsed and +// when the topic is edge-triggered and carries no boolean state. type State uint8 const ( @@ -71,7 +57,6 @@ const ( StateInactive ) -// String implements fmt.Stringer. func (s State) String() string { switch s { case StateUnknown: @@ -85,11 +70,9 @@ func (s State) String() string { } } -// PropertyOperation mirrors the ONVIF wsnt:PropertyOperation attribute and -// indicates whether a message is the first sighting of a property -// (Initialized), a transition (Changed) or the property going away -// (Deleted). PropertyUnknown is used both when the attribute is absent on -// the wire (the spec allows it) and when the value is unrecognised. +// PropertyOperation mirrors the wsnt:PropertyOperation attribute. +// PropertyUnknown covers both "absent on the wire" (the attribute is +// optional) and "unrecognised value". type PropertyOperation uint8 const ( @@ -99,7 +82,6 @@ const ( PropertyDeleted ) -// String implements fmt.Stringer. func (p PropertyOperation) String() string { switch p { case PropertyUnknown: @@ -117,62 +99,32 @@ func (p PropertyOperation) String() string { // Event is a single normalized notification from an ONVIF device. // -// Kind, State and Operation are the normalized fields most callers should -// switch on. Topic, Source and Data preserve the original ONVIF data so -// callers can inspect the wire form without re-parsing SOAP. -// -// Source and Data are maps from ONVIF SimpleItem Name to Value because -// notifications can carry multiple items: AXIS Object Analytics for -// example emits active, classType and confidence in the same Data list, -// and standard DigitalInput notifications carry both InputToken in Source +// Source and Data are maps because ONVIF notifications can carry +// multiple SimpleItems — AXIS Object Analytics emits active+classType+ +// confidence in one Data list, DigitalInput carries InputToken in Source // and LogicalState in Data. type Event struct { - // Kind is the normalized event category. - Kind Kind - // State is the active/inactive value carried by a boolean event. - // StateUnknown for edge-triggered events (LineDetector/Crossed) that - // carry no boolean property. - State State - // Operation is the ONVIF property lifecycle - // (Initialized/Changed/Deleted). + Kind Kind + State State Operation PropertyOperation - // DeviceID identifies the camera that produced the event. Set by the - // Stream from the caller-supplied identifier so a single channel can - // fan in events from multiple devices. - DeviceID string - // Source is the ONVIF Source SimpleItem map (e.g. InputToken, - // VideoSourceConfigurationToken, Rule). Empty when the notification - // has no Source section. - Source map[string]string - // Data is the ONVIF Data SimpleItem map (e.g. IsMotion, LogicalState, - // active, classType). Empty when the notification has no Data - // section. - Data map[string]string - // Topic is the raw ONVIF topic string, e.g. - // tns1:VideoSource/MotionAlarm. - Topic string - // Timestamp is when the stream observed the event locally. + DeviceID string + Source map[string]string + Data map[string]string + Topic string Timestamp time.Time - // DeviceTime is the camera-reported wsnt:UtcTime, when present and - // parseable. Zero if the camera omits the attribute or sends an - // unparseable value. Many cameras have drifting clocks; prefer - // Timestamp for ordering and DeviceTime only for forensics or - // cross-camera correlation when caller manages NTP. + // DeviceTime is the camera-reported wsnt:UtcTime. Cameras drift — + // prefer Timestamp for ordering and DeviceTime only for forensics or + // cross-camera correlation when the caller manages NTP. DeviceTime time.Time // AfterReconnect is true for events delivered after the Stream - // silently recreated its pull-point subscription. ONVIF cameras - // replay each property's current value with PropertyInitialized on - // a new subscription, which would otherwise look like a flood of - // new state changes to a consumer doing edge-detection. Watch this - // flag to suppress duplicate handling, or treat it as a normal - // event if you only care about steady-state level. Cleared on the - // first event whose Operation is not PropertyInitialized. + // silently recreated its subscription. Cameras replay current state + // with PropertyInitialized on a new subscription; watch this flag to + // suppress duplicate edge-detection. Cleared on the first non- + // Initialized event. AfterReconnect bool } -// Op identifies which Stream operation failed. Used by ErrPullFailed, -// ErrRenewFailed and ErrRecreateFailed so consumers can branch with -// errors.As without parsing the wrapped message. +// Op identifies which Stream operation failed. type Op string const ( @@ -182,26 +134,24 @@ const ( ) // ErrPullFailed wraps a transient PullMessages failure. The pull loop -// surfaces it on the Errors channel and continues. Consumers can match -// with errors.As(err, &stream.ErrPullFailed{}). +// surfaces it and continues. type ErrPullFailed struct{ Err error } func (e ErrPullFailed) Error() string { return fmt.Sprintf("pull messages: %v", e.Err) } func (e ErrPullFailed) Unwrap() error { return e.Err } func (ErrPullFailed) Op() Op { return OpPull } -// ErrRenewFailed wraps a Renew SOAP failure. Renew errors are usually -// recovered implicitly: the subscription dies, pull starts failing, -// and the reconnect logic recreates it. +// ErrRenewFailed wraps a Renew SOAP failure. Recovered implicitly: a +// permanently failing renew lets the subscription die, pull starts +// failing, and the reconnect path recreates it. type ErrRenewFailed struct{ Err error } func (e ErrRenewFailed) Error() string { return fmt.Sprintf("renew pull point: %v", e.Err) } func (e ErrRenewFailed) Unwrap() error { return e.Err } func (ErrRenewFailed) Op() Op { return OpRenew } -// ErrRecreateFailed wraps a failed CreatePullPointSubscription during -// the reconnect path. The loop continues with exponential backoff; -// consumers seeing this repeatedly should consider the camera offline. +// ErrRecreateFailed wraps a failed CreatePullPointSubscription. Consumers +// seeing this repeatedly should consider the camera offline. type ErrRecreateFailed struct{ Err error } func (e ErrRecreateFailed) Error() string { return fmt.Sprintf("recreate pull point: %v", e.Err) } From 70e6765a7d44b5960460e91d4fbbdac405137d8a Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Thu, 21 May 2026 20:48:15 +0200 Subject: [PATCH 46/53] fix(event/stream): bound Close drain to survive a hung HTTP caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concurrency audit (third review) flagged that caller.SendSoap is not ctx-aware: cancelling ctx does not unblock a pull or renew goroutine parked in the underlying http.Client.Do. The previous Close() unconditionally did <-s.done before its 5s unsubscribe timeout, so a wedged SendSoap could hang Close indefinitely — taking the agent's shutdown down with it. Adds closeDrainTimeout (5s) to bound the wait for the run goroutines to exit. When the drain times out: * Close returns a 'did not drain' error so the caller can move on. * Unsubscribe is skipped; the subscription expires at the camera once InitialTermination elapses without a Renew. * The wedged goroutines exit later, when the HTTP transport eventually gives up. They are effectively leaked until then — documented in the caller interface comment as the contract callers must accept (or fix, by configuring an http.Client.Timeout). The caller interface doc-comment now states both invariants explicitly: must be goroutine-safe AND must enforce its own per- request timeout, because we cannot from here. Test ---- TestClose_BoundedWhenLoopsStuckOnHungHTTP: drives the fakeCaller with blockAllSendSoap (new flag) so every SendSoap parks. Waits for pullLoop to actually reach the blocked SendSoap before calling Close (a race the previous attempt had: Close raced the loop and exited via the ctx pre-check). Asserts Close returns within closeDrainTimeout + 2s slack with a drain-timeout error. Other concurrency audit findings disposition -------------------------------------------- * unsubscribe goroutine leaks past 5s: intentional, already documented at closeUnsubscribeTimeout. * now func() time.Time data race: written once before goroutines start; safe by happens-before. Tests do not swap it today. * closeOnce self-deadlock if Close called from inside a loop: no path exists; not exposed via the API. --- event/stream/stream.go | 40 ++++++++++++++++++++++----- event/stream/stream_test.go | 55 +++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/event/stream/stream.go b/event/stream/stream.go index e548d3c..9e39f06 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -10,6 +10,14 @@ import ( "github.com/kerberos-io/onvif" ) +// closeDrainTimeout bounds Close's wait for the pull and renew +// goroutines to exit. The loops block in caller.SendSoap which is not +// ctx-aware (the underlying http.Client is the only thing that can +// unblock them — see caller below). On a hung HTTP transport Close +// would otherwise wait forever; instead it returns an error and lets +// the calling agent move on. +const closeDrainTimeout = 5 * time.Second + // closeUnsubscribeTimeout bounds the Unsubscribe SOAP call issued by // Close. A subscription expires at the camera once InitialTermination // elapses without a renew, so a missed unsubscribe is at worst @@ -103,8 +111,17 @@ func (o Options) withDefaults() Options { } // caller is the *onvif.Device subset Stream depends on. Implementations -// must be safe for concurrent use — pull and renew goroutines call in -// from separate goroutines. *onvif.Device satisfies this via http.Client. +// must: +// +// - Be safe for concurrent use — pull and renew goroutines call in +// from separate goroutines. *onvif.Device satisfies this via +// http.Client. +// - Enforce a per-request timeout via the underlying HTTP client. +// The methods do not take a ctx, so ctx-cancel cannot interrupt a +// hung request; only the HTTP client's own timeout can. Close +// bounds its drain wait at closeDrainTimeout to survive a misbehaving +// caller, but a leaking goroutine remains until the HTTP call +// eventually returns. type caller interface { CallMethod(method any) (*http.Response, error) SendSoap(endpoint, body string) (*http.Response, error) @@ -194,15 +211,24 @@ func (s *Stream) Events() <-chan Event { return s.events } // when the Stream stops. func (s *Stream) Errors() <-chan error { return s.errors } -// Close stops the background goroutines, waits for them to exit and -// Unsubscribes from the camera. Subsequent calls are no-ops. +// Close stops the background goroutines, waits up to closeDrainTimeout +// for them to exit, and then Unsubscribes from the camera (also bounded, +// by closeUnsubscribeTimeout). Subsequent calls are no-ops. // -// Unsubscribe is bounded by closeUnsubscribeTimeout so a hung camera -// connection cannot wedge the caller. +// If the drain times out the goroutines are likely wedged inside a +// non-ctx-aware caller.SendSoap; they will exit on their own once the +// HTTP call returns. Unsubscribe is skipped in that case — the +// subscription expires at the camera anyway. func (s *Stream) Close() error { s.closeOnce.Do(func() { s.cancel() - <-s.done + + select { + case <-s.done: + case <-time.After(closeDrainTimeout): + s.closeErr = fmt.Errorf("close: pull/renew loops did not drain within %s (likely stuck in caller HTTP)", closeDrainTimeout) + return + } errCh := make(chan error, 1) go func() { diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index 062cac7..f5da324 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -22,8 +22,9 @@ import ( // require tests to enumerate every call. // // blockUnsubscribe, when non-nil, causes SendSoap calls whose body -// contains "Unsubscribe" to block until the channel is closed. Used to -// verify Close's timeout path. +// contains "Unsubscribe" to block until the channel is closed. +// blockAllSendSoap, when non-nil, blocks every SendSoap call until +// closed (simulates a hung HTTP transport). type fakeCaller struct { mu sync.Mutex callMethodResps []fakeResp @@ -33,6 +34,7 @@ type fakeCaller struct { callMethodCalls []any sendSoapCalls [][2]string blockUnsubscribe chan struct{} + blockAllSendSoap chan struct{} } type fakeResp struct { @@ -84,8 +86,12 @@ func (f *fakeCaller) SendSoap(endpoint, body string) (*http.Response, error) { f.sendSoapResps = f.sendSoapResps[1:] } block := f.blockUnsubscribe + blockAll := f.blockAllSendSoap f.mu.Unlock() + if blockAll != nil { + <-blockAll + } if block != nil && strings.Contains(body, "Unsubscribe") { <-block } @@ -444,3 +450,48 @@ func TestFakeCaller_QueueThenDefaultFallback(t *testing.T) { assert.Contains(t, string(b3[:n]), "PullMessagesResponse", "default SendSoap should be an empty PullMessagesResponse envelope") } + +func TestClose_BoundedWhenLoopsStuckOnHungHTTP(t *testing.T) { + // Simulates a hung HTTP transport: every SendSoap blocks + // indefinitely. The pull and renew loops are wedged inside + // SendSoap and ctx-cancel cannot unblock them. Close must still + // return within its bounded budget so the agent's shutdown does + // not hang. + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + blockAll := make(chan struct{}) + defer close(blockAll) + fc.mu.Lock() + fc.blockAllSendSoap = blockAll + fc.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s, err := newStream(ctx, fc, Options{ + PullTimeout: 100 * time.Millisecond, + InitialTermination: 30 * time.Second, + }) + require.NoError(t, err) + + // Wait until pullLoop is actually parked inside the blocked + // SendSoap. Without this, Close races with the loop's first + // iteration and exits via the ctx pre-check instead of + // exercising the drain-timeout path. + deadline := time.Now().Add(time.Second) + for time.Now().Before(deadline) && fc.sendSoapCallCount() == 0 { + time.Sleep(10 * time.Millisecond) + } + require.GreaterOrEqual(t, fc.sendSoapCallCount(), 1, "pullLoop never reached SendSoap") + + start := time.Now() + err = s.Close() + elapsed := time.Since(start) + + require.Error(t, err) + assert.Contains(t, err.Error(), "drain", "expected a drain-timeout error") + // Total budget is closeDrainTimeout for the wait + ~0 for unsubscribe + // (which is skipped when drain times out). Give plenty of slack for + // scheduling on a loaded CI machine. + assert.Less(t, elapsed, closeDrainTimeout+2*time.Second, + "Close exceeded bound (%s); expected ~%s", elapsed, closeDrainTimeout) +} From a796a230582786de6901fdc1733eff6397aa5b46 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:56 +0200 Subject: [PATCH 47/53] feat(event/stream): enrich SOAP transport errors with fault detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull/renew/recreate/unsubscribe errors against a misbehaving camera previously surfaced as "Post with digest error: 400: 400 Bad Request" — operators had no way to tell auth failure from a malformed request from an expired subscription. The body always carried the answer; we just discarded it. enrichSOAPErr now reads the response body alongside transport errors and appends, in order of preference: 1. SOAP 1.1 faultstring / SOAP 1.2 Reason/Text — the reason text. 2. SOAP 1.2 Subcode/Value — AXIS routinely sends an empty Text and leaves Subcode as the only actionable signal (ter:InvalidArgs etc). 3. Raw body excerpt, truncated to maxErrExcerpt — for non-SOAP bodies (HTML error pages, plain text) without flooding logs. The original error is preserved via %w so errors.Is/As in logStreamError keep working. Wired into all four SOAP call sites (createPullPoint, pullMessages, renewPullPoint, unsubscribePullPoint). --- event/stream/renew.go | 2 +- event/stream/renew_test.go | 19 +++++ event/stream/soap.go | 55 +++++++++++++- event/stream/soap_test.go | 140 ++++++++++++++++++++++++++++++++++++ event/stream/stream_test.go | 10 +-- 5 files changed, 218 insertions(+), 8 deletions(-) diff --git a/event/stream/renew.go b/event/stream/renew.go index cd3f199..bf4bdb8 100644 --- a/event/stream/renew.go +++ b/event/stream/renew.go @@ -51,7 +51,7 @@ func renewPullPoint(c caller, endpoint string, opts Options) error { } resp, err := c.SendSoap(endpoint, string(body)) if err != nil { - return err + return enrichSOAPErr(resp, err) } _, err = readClose(resp) return err diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go index cc758bb..551d241 100644 --- a/event/stream/renew_test.go +++ b/event/stream/renew_test.go @@ -2,6 +2,7 @@ package stream import ( "context" + "errors" "strings" "testing" "time" @@ -168,3 +169,21 @@ func TestRenew_SendsAbsoluteDateTimeNotDuration(t *testing.T) { assert.NotContains(t, renewBody, "PT", "Renew should not send relative duration; some firmwares reject it") assert.Regexp(t, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`, renewBody, "Renew should send absolute RFC3339 UTC") } + +// --- Wiring: renew surfaces SOAP fault detail ------------------------- + +func TestRenewPullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { + fc := newFakeCaller() + fc.queueSendSoap(renewFaultBody, errors.New("400 Bad Request")) + err := renewPullPoint(fc, "http://camera/sub", defaultOptions()) + require.Error(t, err) + assert.Contains(t, err.Error(), "renew-specific complaint", + "renewPullPoint must enrich transport errors with the camera's SOAP fault") +} + +const renewFaultBody = ` + + env:Sender + renew-specific complaint + +` diff --git a/event/stream/soap.go b/event/stream/soap.go index 879b22c..efb9438 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -35,7 +35,7 @@ func createPullPoint(c caller, opts Options) (string, error) { } resp, err := c.CallMethod(req) if err != nil { - return "", err + return "", enrichSOAPErr(resp, err) } body, err := readClose(resp) if err != nil { @@ -65,7 +65,7 @@ func pullMessages(c caller, endpoint string, opts Options) ([]event.Notification } resp, err := c.SendSoap(endpoint, string(body)) if err != nil { - return nil, err + return nil, enrichSOAPErr(resp, err) } respBody, err := readClose(resp) if err != nil { @@ -90,7 +90,7 @@ func unsubscribePullPoint(c caller, endpoint string) error { } resp, err := c.SendSoap(endpoint, string(body)) if err != nil { - return err + return enrichSOAPErr(resp, err) } _, err = readClose(resp) return err @@ -148,6 +148,8 @@ var ( soap11FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?faultstring[^>]*>(.*?)\s]+:)?faultstring>`) // SOAP 1.2: ...reason... soap12FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Reason\b[^>]*>.*?<(?:[^:>\s]+:)?Text[^>]*>(.*?)\s]+:)?Text>`) + // SOAP 1.2 Subcode: ...ter:InvalidArgs... + soap12SubcodeRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Subcode\b[^>]*>.*?<(?:[^:>\s]+:)?Value[^>]*>(.*?)\s]+:)?Value>`) ) // extractSOAPFault returns the reason text from a SOAP fault or empty @@ -166,6 +168,53 @@ func extractSOAPFault(body string) string { return "" } +// extractSOAPSubcode is the fallback when Reason/Text is empty — AXIS +// routinely sends an empty alongside a populated Subcode +// (e.g. "ter:InvalidArgs"), and that subcode is the only actionable +// signal the operator gets. +func extractSOAPSubcode(body string) string { + m := soap12SubcodeRE.FindStringSubmatch(body) + if len(m) > 1 { + return strings.TrimSpace(m[1]) + } + return "" +} + +// maxErrExcerpt caps the body excerpt appended to an enriched error so +// a wedged camera streaming a multi-megabyte HTML error page can not +// flood logs with every retry. +const maxErrExcerpt = 512 + +// enrichSOAPErr appends the camera's actual complaint (SOAP Fault +// reason, then Subcode, then raw body excerpt) to a transport error so +// operators see *why* the camera said 400 instead of just "400 Bad +// Request". The original err is preserved via %w for errors.Is/As. +func enrichSOAPErr(resp *http.Response, err error) error { + if err == nil { + return nil + } + if resp == nil || resp.Body == nil { + return err + } + defer resp.Body.Close() + b, readErr := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) + if readErr != nil || len(b) == 0 { + return err + } + body := string(b) + if reason := extractSOAPFault(body); reason != "" { + return fmt.Errorf("%w: SOAP fault: %s", err, reason) + } + if sub := extractSOAPSubcode(body); sub != "" { + return fmt.Errorf("%w: SOAP fault subcode: %s", err, sub) + } + excerpt := strings.TrimSpace(body) + if len(excerpt) > maxErrExcerpt { + excerpt = excerpt[:maxErrExcerpt] + "...(truncated)" + } + return fmt.Errorf("%w: response body: %s", err, excerpt) +} + // durationToXSD formats a duration as xsd:duration PTnS. Second // precision is sufficient — ONVIF cameras do not honour sub-second // pull timeouts. diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index 7c5b6c2..cd6d54f 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -2,6 +2,9 @@ package stream import ( "context" + "errors" + "io" + "net/http" "strings" "testing" @@ -84,3 +87,140 @@ func testContext(t *testing.T) context.Context { t.Cleanup(cancel) return ctx } + +// --- Error enrichment from SOAP response bodies ---------------------- + +func fakeResponse(body string) *http.Response { + return &http.Response{ + StatusCode: http.StatusBadRequest, + Body: io.NopCloser(strings.NewReader(body)), + } +} + +func TestEnrichSOAPErr_NilErrReturnsNil(t *testing.T) { + assert.NoError(t, enrichSOAPErr(fakeResponse("anything"), nil)) +} + +func TestEnrichSOAPErr_NilRespPreservesOriginal(t *testing.T) { + orig := errors.New("transport boom") + got := enrichSOAPErr(nil, orig) + assert.ErrorIs(t, got, orig) + assert.Equal(t, orig.Error(), got.Error(), "no body, no extra context to add") +} + +func TestEnrichSOAPErr_SOAP11FaultStringAppearsInError(t *testing.T) { + body := ` + not authorized +` + got := enrichSOAPErr(fakeResponse(body), errors.New("400 Bad Request")) + require.Error(t, got) + assert.Contains(t, got.Error(), "400 Bad Request") + assert.Contains(t, got.Error(), "not authorized") +} + +func TestEnrichSOAPErr_SOAP12ReasonAppearsInError(t *testing.T) { + body := ` + + env:Sender + Subscription has expired + +` + got := enrichSOAPErr(fakeResponse(body), errors.New("400 Bad Request")) + require.Error(t, got) + assert.Contains(t, got.Error(), "Subscription has expired") +} + +// Pins the AXIS case: a Fault with populated Subcode but an empty +// . Without subcode fallback, the only signal +// the operator sees is "400 Bad Request". +func TestEnrichSOAPErr_EmptyReasonFallsBackToSubcode(t *testing.T) { + body := ` + + + SOAP-ENV:Sender + ter:InvalidArgs + + + +` + got := enrichSOAPErr(fakeResponse(body), errors.New("Post with digest error: 400: 400 Bad Request")) + require.Error(t, got) + assert.Contains(t, got.Error(), "ter:InvalidArgs", + "AXIS-style empty-Reason Faults must surface their Subcode") +} + +func TestEnrichSOAPErr_NonFaultBodyIncludesExcerpt(t *testing.T) { + body := `404 Not Found — /onvif/services missing` + got := enrichSOAPErr(fakeResponse(body), errors.New("404 Not Found")) + require.Error(t, got) + assert.Contains(t, got.Error(), "/onvif/services missing") +} + +func TestEnrichSOAPErr_LargeNonFaultBodyTruncated(t *testing.T) { + // A misbehaving camera could stream a multi-megabyte body. The + // helper must cap the excerpt so a wedged camera does not flood + // logs. + body := strings.Repeat("X", 8192) + got := enrichSOAPErr(fakeResponse(body), errors.New("500")) + require.Error(t, got) + assert.Less(t, len(got.Error()), 2048, + "enriched error must stay log-line sized even on huge bodies") +} + +func TestEnrichSOAPErr_PreservesOriginalForErrorsIs(t *testing.T) { + // Callers wrap pull/renew/recreate errors with errors.As in + // logStreamError; enrichment must keep the original wrappable. + orig := errors.New("sentinel") + got := enrichSOAPErr(fakeResponse(`x`), orig) + assert.ErrorIs(t, got, orig) +} + +// --- Subcode extraction ---------------------------------------------- + +func TestExtractSOAPSubcode_Present(t *testing.T) { + body := ` + SOAP-ENV:Sender + ter:InvalidArgs +` + assert.Equal(t, "ter:InvalidArgs", extractSOAPSubcode(body)) +} + +func TestExtractSOAPSubcode_Absent(t *testing.T) { + assert.Empty(t, extractSOAPSubcode(`env:Sender`)) +} + +// --- Wiring: each SOAP call site routes errors through enrichSOAPErr - + +const faultBody = ` + + env:Sender + camera-specific complaint + +` + +func TestCreatePullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(faultBody, errors.New("400 Bad Request")) + _, err := createPullPoint(fc, defaultOptions()) + require.Error(t, err) + assert.Contains(t, err.Error(), "camera-specific complaint", + "createPullPoint must enrich transport errors with the camera's SOAP fault") +} + +func TestPullMessages_EnrichesTransportErrWithFaultReason(t *testing.T) { + fc := newFakeCaller() + fc.queueSendSoap(faultBody, errors.New("400 Bad Request")) + _, err := pullMessages(fc, "http://camera/sub", defaultOptions()) + require.Error(t, err) + assert.Contains(t, err.Error(), "camera-specific complaint", + "pullMessages must enrich transport errors with the camera's SOAP fault") +} + +func TestUnsubscribePullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { + fc := newFakeCaller() + fc.queueSendSoap(faultBody, errors.New("400 Bad Request")) + err := unsubscribePullPoint(fc, "http://camera/sub") + require.Error(t, err) + assert.Contains(t, err.Error(), "camera-specific complaint", + "unsubscribePullPoint must enrich transport errors with the camera's SOAP fault") +} diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index f5da324..f58aa0c 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -71,10 +71,12 @@ func (f *fakeCaller) CallMethod(m any) (*http.Response, error) { r = f.callMethodResps[0] f.callMethodResps = f.callMethodResps[1:] } - if r.err != nil { + // Mirror networking.SendSoap*: a 4xx/5xx returns body alongside + // err. Tests opt into that shape by queueing body + err together. + if r.err != nil && r.body == "" { return nil, r.err } - return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, nil + return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, r.err } func (f *fakeCaller) SendSoap(endpoint, body string) (*http.Response, error) { @@ -96,10 +98,10 @@ func (f *fakeCaller) SendSoap(endpoint, body string) (*http.Response, error) { <-block } - if r.err != nil { + if r.err != nil && r.body == "" { return nil, r.err } - return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, nil + return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, r.err } func (f *fakeCaller) sendSoapCallCount() int { From de3f049a6304f9dde93f4b5f8009abaf52c8376c Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:56 +0200 Subject: [PATCH 48/53] feat(event/stream): echo WS-Addressing ReferenceParameters for AXIS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AXIS encodes pull-point subscription identity in inside the CreatePullPointSubscription response — a generic /onvif/services endpoint plus a child — rather than a per-subscription URL. We were discarding the ReferenceParameters and POSTing to the generic endpoint, which AXIS rejected with ter:InvalidArgs on every PullMessages/Renew/Unsubscribe. Per WS-Addressing 1.0 §3.1 each reference parameter MUST be echoed as a SOAP Header block carrying wsa:IsReferenceParameter="true". - subscriptionRef now carries Address + the verbatim ReferenceParameters inner XML extracted from the create response. - buildRefParamsHeader walks the children, adds the attribute, and produces the SOAP Header content. - pullMessages, renewPullPoint, unsubscribePullPoint switch from SendSoap to a new SendSoapWithHeader path on the caller interface. - onvif.Device gains SendSoapWithHeader as a thin variant of SendSoap (existing SendSoap is now a one-liner delegating to it with empty header content, so all external callers are unaffected). Verified end-to-end against an AXIS camera at 192.168.1.10: pulls now stream the full topic tree (VMD, Object Analytics, IO, storage, hardware-failure topics) instead of looping on ter:InvalidArgs. --- Device.go | 14 ++- Device_test.go | 49 ++++++++++ event/stream/reconnect.go | 4 +- event/stream/renew.go | 8 +- event/stream/renew_test.go | 16 +++- event/stream/soap.go | 80 ++++++++++++++--- event/stream/soap_test.go | 172 +++++++++++++++++++++++++++++++++++- event/stream/stream.go | 28 ++++-- event/stream/stream_test.go | 11 +++ 9 files changed, 352 insertions(+), 30 deletions(-) diff --git a/Device.go b/Device.go index c7b0cce..a9749be 100644 --- a/Device.go +++ b/Device.go @@ -335,26 +335,32 @@ func (dev *Device) GetEndpointByRequestStruct(requestStruct interface{}) (string // CallMethod functions call an method, defined struct with authentication data func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Response, error) { + return dev.SendSoapWithHeader(endpoint, xmlRequestBody, "") +} +// SendSoapWithHeader is SendSoap plus arbitrary inner-Header XML — +// needed to echo WS-Addressing ReferenceParameters (with +// wsa:IsReferenceParameter="true") back to vendors like AXIS that +// identify pull-point subscriptions through them rather than the URL. +func (dev Device) SendSoapWithHeader(endpoint, xmlRequestBody, xmlHeaderContent string) (*http.Response, error) { soap := gosoap.NewEmptySOAP() soap.AddStringBodyContent(xmlRequestBody) soap.AddRootNamespaces(Xlmns) soap.AddAction() - - //Auth Handling + if xmlHeaderContent != "" { + _ = soap.AddStringHeaderContent(xmlHeaderContent) + } if dev.params.Username != "" && dev.params.Password != "" { soap.AddWSSecurity(dev.params.Username, dev.params.Password) } servResp, err := networking.SendSoap(dev.params.HttpClient, endpoint, soap.String()) if err != nil { - // Close server response body to reuse the connection if servResp != nil { servResp.Body.Close() } servResp, err = networking.SendSoapWithDigest(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password) } - return servResp, err } diff --git a/Device_test.go b/Device_test.go index f8bfe04..8577e85 100644 --- a/Device_test.go +++ b/Device_test.go @@ -1,9 +1,14 @@ package onvif import ( + "io" + "net/http" + "net/http/httptest" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDevice_SetDeviceInfoFromScopes(t *testing.T) { @@ -22,3 +27,47 @@ func TestDevice_SetDeviceInfoFromScopes(t *testing.T) { assert.Equal(t, device.info.Name, name) assert.Equal(t, device.info.Model, hardware) } + +// TestDevice_SendSoapWithHeader_InjectsHeaderXML verifies that the +// supplied header XML lands inside the SOAP
element of the +// outgoing request. AXIS-style WS-Addressing reference parameter +// echoing depends on this — without it the camera returns +// ter:InvalidArgs on every PullMessages. +func TestDevice_SendSoapWithHeader_InjectsHeaderXML(t *testing.T) { + const headerXML = `297` + const bodyXML = `PT5S32` + + var captured string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + captured = string(b) + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + + dev := Device{ + params: DeviceParams{ + Xaddr: strings.TrimPrefix(srv.URL, "http://"), + HttpClient: srv.Client(), + }, + } + resp, err := dev.SendSoapWithHeader(srv.URL, bodyXML, headerXML) + require.NoError(t, err) + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + + headerStart := strings.Index(captured, "Header>") + headerEnd := strings.Index(captured, "; got: %s", captured) + assert.Greater(t, headerEnd, headerStart, "envelope must close the header") + + headerSlice := captured[headerStart:strings.Index(captured, "Body>")] + assert.Contains(t, headerSlice, "SubscriptionId", + "injected header element must land inside SOAP
") + assert.Contains(t, headerSlice, "297") + + bodySlice := captured[strings.Index(captured, "Body>"):] + assert.Contains(t, bodySlice, "PullMessages", + "body content must land inside SOAP ") +} diff --git a/event/stream/reconnect.go b/event/stream/reconnect.go index 76bd791..2d0e413 100644 --- a/event/stream/reconnect.go +++ b/event/stream/reconnect.go @@ -74,7 +74,7 @@ func (s *Stream) pullLoop(ctx context.Context) { // attemptRecreate returns (justRecreated, cont). cont is false only // when ctx cancelled during backoff so the caller exits the loop. func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *time.Duration) (justRecreated, cont bool) { - addr, err := createPullPoint(s.caller, s.opts) + ref, err := createPullPoint(s.caller, s.opts) if err != nil { s.surfaceError(ErrRecreateFailed{Err: err}) if !sleepCtx(ctx, jitter(*backoff)) { @@ -86,7 +86,7 @@ func (s *Stream) attemptRecreate(ctx context.Context, failures *int, backoff *ti } return false, true } - s.setPullPoint(addr) + s.setPullPoint(ref) *failures = 0 *backoff = s.opts.RetryBackoff return true, true diff --git a/event/stream/renew.go b/event/stream/renew.go index bf4bdb8..ef0280d 100644 --- a/event/stream/renew.go +++ b/event/stream/renew.go @@ -42,14 +42,18 @@ func (s *Stream) renewLoop(ctx context.Context) { // WS-BaseNotification §6.1.1 also allows xsd:duration but older // Hikvision, some Dahua and some Bosch firmwares reject the // relative form. -func renewPullPoint(c caller, endpoint string, opts Options) error { +func renewPullPoint(c caller, ref subscriptionRef, opts Options) error { absoluteEnd := time.Now().UTC().Add(opts.InitialTermination).Format("2006-01-02T15:04:05Z") req := event.Renew{TerminationTime: xsd.String(absoluteEnd)} body, err := xml.Marshal(req) if err != nil { return fmt.Errorf("marshal Renew: %w", err) } - resp, err := c.SendSoap(endpoint, string(body)) + headerXML, err := buildRefParamsHeader(ref.RefParamsXML) + if err != nil { + return fmt.Errorf("build ref params header: %w", err) + } + resp, err := c.SendSoapWithHeader(ref.Address, string(body), headerXML) if err != nil { return enrichSOAPErr(resp, err) } diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go index 551d241..f9c0a12 100644 --- a/event/stream/renew_test.go +++ b/event/stream/renew_test.go @@ -175,7 +175,7 @@ func TestRenew_SendsAbsoluteDateTimeNotDuration(t *testing.T) { func TestRenewPullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { fc := newFakeCaller() fc.queueSendSoap(renewFaultBody, errors.New("400 Bad Request")) - err := renewPullPoint(fc, "http://camera/sub", defaultOptions()) + err := renewPullPoint(fc, subscriptionRef{Address: "http://camera/sub"}, defaultOptions()) require.Error(t, err) assert.Contains(t, err.Error(), "renew-specific complaint", "renewPullPoint must enrich transport errors with the camera's SOAP fault") @@ -187,3 +187,17 @@ const renewFaultBody = `renew-specific complaint ` + +func TestRenewPullPoint_EchoesRefParamsWithIsReferenceParameter(t *testing.T) { + ref := subscriptionRef{ + Address: "http://192.168.1.10/onvif/services", + RefParamsXML: `297`, + } + fc := newFakeCaller() + require.NoError(t, renewPullPoint(fc, ref, defaultOptions())) + require.Len(t, fc.sendSoapHeaders, 1) + hdr := fc.sendSoapHeaders[0] + assert.Contains(t, hdr, "SubscriptionId") + assert.Contains(t, hdr, "297") + assert.Contains(t, hdr, `IsReferenceParameter="true"`) +} diff --git a/event/stream/soap.go b/event/stream/soap.go index efb9438..17ab885 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/beevik/etree" "github.com/kerberos-io/onvif/event" "github.com/kerberos-io/onvif/xsd" ) @@ -22,7 +23,7 @@ import ( // hostile or buggy camera from OOMing the process. const maxResponseBytes = 10 << 20 -func createPullPoint(c caller, opts Options) (string, error) { +func createPullPoint(c caller, opts Options) (subscriptionRef, error) { term := xsd.String(durationToXSD(opts.InitialTermination)) req := event.CreatePullPointSubscription{InitialTerminationTime: &term} if opts.RawTopicFilter != "" { @@ -35,26 +36,26 @@ func createPullPoint(c caller, opts Options) (string, error) { } resp, err := c.CallMethod(req) if err != nil { - return "", enrichSOAPErr(resp, err) + return subscriptionRef{}, enrichSOAPErr(resp, err) } body, err := readClose(resp) if err != nil { - return "", err + return subscriptionRef{}, err } var decoded event.CreatePullPointSubscriptionResponse if err := unmarshalNode(body, "CreatePullPointSubscriptionResponse", &decoded); err != nil { - return "", err + return subscriptionRef{}, err } addr := string(decoded.SubscriptionReference.Address) if addr == "" { - return "", errors.New("CreatePullPointSubscription response has empty SubscriptionReference Address") + return subscriptionRef{}, errors.New("CreatePullPointSubscription response has empty SubscriptionReference Address") } - return addr, nil + return subscriptionRef{Address: addr, RefParamsXML: extractReferenceParameters(body)}, nil } // pullMessages returns an empty slice (no error) when the camera had // nothing within PullTimeout. -func pullMessages(c caller, endpoint string, opts Options) ([]event.NotificationMessage, error) { +func pullMessages(c caller, ref subscriptionRef, opts Options) ([]event.NotificationMessage, error) { req := event.PullMessages{ Timeout: xsd.Duration(durationToXSD(opts.PullTimeout)), MessageLimit: xsd.Int(opts.MessageLimit), @@ -63,7 +64,11 @@ func pullMessages(c caller, endpoint string, opts Options) ([]event.Notification if err != nil { return nil, fmt.Errorf("marshal PullMessages: %w", err) } - resp, err := c.SendSoap(endpoint, string(body)) + headerXML, err := buildRefParamsHeader(ref.RefParamsXML) + if err != nil { + return nil, fmt.Errorf("build ref params header: %w", err) + } + resp, err := c.SendSoapWithHeader(ref.Address, string(body), headerXML) if err != nil { return nil, enrichSOAPErr(resp, err) } @@ -78,17 +83,21 @@ func pullMessages(c caller, endpoint string, opts Options) ([]event.Notification return decoded.NotificationMessage, nil } -// unsubscribePullPoint is best-effort. Empty endpoint is a no-op -// (construction failed before installing one). -func unsubscribePullPoint(c caller, endpoint string) error { - if endpoint == "" { +// unsubscribePullPoint is best-effort. Empty Address is a no-op +// (construction failed before installing a subscription). +func unsubscribePullPoint(c caller, ref subscriptionRef) error { + if ref.Address == "" { return nil } body, err := xml.Marshal(event.Unsubscribe{}) if err != nil { return fmt.Errorf("marshal Unsubscribe: %w", err) } - resp, err := c.SendSoap(endpoint, string(body)) + headerXML, err := buildRefParamsHeader(ref.RefParamsXML) + if err != nil { + return fmt.Errorf("build ref params header: %w", err) + } + resp, err := c.SendSoapWithHeader(ref.Address, string(body), headerXML) if err != nil { return enrichSOAPErr(resp, err) } @@ -168,6 +177,51 @@ func extractSOAPFault(body string) string { return "" } +var refParamsRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?ReferenceParameters\b[^>]*>(.*?)\s]+:)?ReferenceParameters>`) + +// buildRefParamsHeader produces the SOAP
inner XML for a set +// of WS-Addressing ReferenceParameters: each top-level child element +// is re-emitted with wsa:IsReferenceParameter="true" added, as the +// spec requires. Empty input yields empty output (no-op for vendors +// that encode subscription identity in the URL). +func buildRefParamsHeader(rawXML string) (string, error) { + if strings.TrimSpace(rawXML) == "" { + return "", nil + } + doc := etree.NewDocument() + if err := doc.ReadFromString("" + rawXML + ""); err != nil { + return "", fmt.Errorf("parse ref params: %w", err) + } + wrap := doc.SelectElement("wrap") + if wrap == nil { + return "", errors.New("parse ref params: missing wrap root") + } + var out strings.Builder + for _, child := range wrap.ChildElements() { + child.CreateAttr("wsa:IsReferenceParameter", "true") + d := etree.NewDocument() + d.SetRoot(child.Copy()) + s, err := d.WriteToString() + if err != nil { + return "", fmt.Errorf("serialise ref param child: %w", err) + } + out.WriteString(strings.TrimRight(s, "\n")) + } + return out.String(), nil +} + +// extractReferenceParameters returns the verbatim inner XML so callers +// can echo it (with wsa:IsReferenceParameter="true") into the SOAP +// Header of subscription-scoped requests per WS-Addressing 1.0 §3.1. +// Without that echo, AXIS rejects PullMessages with ter:InvalidArgs. +func extractReferenceParameters(body string) string { + m := refParamsRE.FindStringSubmatch(body) + if len(m) < 2 { + return "" + } + return strings.TrimSpace(m[1]) +} + // extractSOAPSubcode is the fallback when Reason/Text is empty — AXIS // routinely sends an empty alongside a populated Subcode // (e.g. "ter:InvalidArgs"), and that subcode is the only actionable diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index cd6d54f..9e2b954 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -210,7 +210,7 @@ func TestCreatePullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { func TestPullMessages_EnrichesTransportErrWithFaultReason(t *testing.T) { fc := newFakeCaller() fc.queueSendSoap(faultBody, errors.New("400 Bad Request")) - _, err := pullMessages(fc, "http://camera/sub", defaultOptions()) + _, err := pullMessages(fc, subscriptionRef{Address: "http://camera/sub"}, defaultOptions()) require.Error(t, err) assert.Contains(t, err.Error(), "camera-specific complaint", "pullMessages must enrich transport errors with the camera's SOAP fault") @@ -219,8 +219,176 @@ func TestPullMessages_EnrichesTransportErrWithFaultReason(t *testing.T) { func TestUnsubscribePullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { fc := newFakeCaller() fc.queueSendSoap(faultBody, errors.New("400 Bad Request")) - err := unsubscribePullPoint(fc, "http://camera/sub") + err := unsubscribePullPoint(fc, subscriptionRef{Address: "http://camera/sub"}) require.Error(t, err) assert.Contains(t, err.Error(), "camera-specific complaint", "unsubscribePullPoint must enrich transport errors with the camera's SOAP fault") } + +// --- ReferenceParameters extraction (WS-Addressing 1.0 §3.1) --------- +// +// AXIS encodes the subscription identity in +// inside CreatePullPointSubscriptionResponse rather than in the URL +// itself. Subsequent PullMessages/Renew/Unsubscribe MUST echo those +// elements verbatim into the SOAP Header, or the camera responds with +// ter:InvalidArgs. The auto-generated event.ReferenceParametersType is +// an empty struct (drops children), so we extract the raw inner XML. + +func TestExtractReferenceParameters_AXISShape(t *testing.T) { + body := ` + + + http://192.168.1.10/onvif/services + + 297 + + + +` + got := extractReferenceParameters(body) + assert.Contains(t, got, "SubscriptionId") + assert.Contains(t, got, "297") + assert.Contains(t, got, `xmlns:dom0="http://www.axis.com/2009/event"`, + "namespace declaration on the SubscriptionId child must survive extraction") +} + +func TestExtractReferenceParameters_AbsentReturnsEmpty(t *testing.T) { + // Geovision/Hikvision-style: Address only, no ReferenceParameters. + body := ` + + + http://camera/onvif/Events/Sub_1 + + +` + assert.Empty(t, extractReferenceParameters(body)) +} + +func TestExtractReferenceParameters_EmptyBodyReturnsEmpty(t *testing.T) { + assert.Empty(t, extractReferenceParameters("")) +} + +// --- createPullPoint returns both address and ref params ------------- + +func TestCreatePullPoint_ReturnsRefParamsAlongsideAddress(t *testing.T) { + body := ` + + + http://192.168.1.10/onvif/services + + 297 + + + +` + fc := newFakeCaller() + fc.queueCallMethod(body, nil) + ref, err := createPullPoint(fc, defaultOptions()) + require.NoError(t, err) + assert.Equal(t, "http://192.168.1.10/onvif/services", ref.Address) + assert.Contains(t, ref.RefParamsXML, "SubscriptionId") + assert.Contains(t, ref.RefParamsXML, "297") +} + +func TestCreatePullPoint_VendorWithoutRefParams_RefParamsEmpty(t *testing.T) { + fc := newFakeCaller() + fc.queueCallMethod(createPullPointResp, nil) + ref, err := createPullPoint(fc, defaultOptions()) + require.NoError(t, err) + assert.NotEmpty(t, ref.Address) + assert.Empty(t, ref.RefParamsXML) +} + +// --- Reference-parameter echoing in subscription-scoped calls -------- +// +// WS-Addressing 1.0 §3.1 requires each child +// to be echoed as a SOAP Header block carrying wsa:IsReferenceParameter +// ="true". AXIS rejects PullMessages with ter:InvalidArgs when this is +// absent. + +func TestPullMessages_EchoesRefParamsWithIsReferenceParameterAttribute(t *testing.T) { + ref := subscriptionRef{ + Address: "http://192.168.1.10/onvif/services", + RefParamsXML: `297`, + } + fc := newFakeCaller() + _, err := pullMessages(fc, ref, defaultOptions()) + require.NoError(t, err) + require.Len(t, fc.sendSoapHeaders, 1) + hdr := fc.sendSoapHeaders[0] + assert.Contains(t, hdr, "SubscriptionId", "ref param element must be echoed") + assert.Contains(t, hdr, "297", "ref param value must be echoed") + assert.Contains(t, hdr, `IsReferenceParameter="true"`, + "WS-Addressing 1.0 §3.1 requires the attribute on each echoed element") +} + +func TestPullMessages_NoRefParams_HeaderEmpty(t *testing.T) { + ref := subscriptionRef{Address: "http://camera/sub", RefParamsXML: ""} + fc := newFakeCaller() + _, err := pullMessages(fc, ref, defaultOptions()) + require.NoError(t, err) + require.Len(t, fc.sendSoapHeaders, 1) + assert.Empty(t, fc.sendSoapHeaders[0], "vendors without ref params get no extra header") +} + +func TestPullMessages_PostsToAddressFromRef(t *testing.T) { + ref := subscriptionRef{Address: "http://camera/specific-sub-endpoint", RefParamsXML: ""} + fc := newFakeCaller() + _, err := pullMessages(fc, ref, defaultOptions()) + require.NoError(t, err) + require.NotEmpty(t, fc.sendSoapCalls) + assert.Equal(t, "http://camera/specific-sub-endpoint", fc.sendSoapCalls[0][0]) +} + +// --- Building the header XML from raw ref params ---------------------- + +func TestBuildRefParamsHeader_AddsIsReferenceParameter(t *testing.T) { + raw := `297` + got, err := buildRefParamsHeader(raw) + require.NoError(t, err) + assert.Contains(t, got, "SubscriptionId") + assert.Contains(t, got, "297") + assert.Contains(t, got, `xmlns:dom0="http://www.axis.com/2009/event"`, + "original namespace declaration must survive") + assert.Contains(t, got, `IsReferenceParameter="true"`) +} + +func TestBuildRefParamsHeader_MultipleTopLevelChildren(t *testing.T) { + raw := `12` + got, err := buildRefParamsHeader(raw) + require.NoError(t, err) + assert.Equal(t, 2, strings.Count(got, `IsReferenceParameter="true"`), + "attribute must be added to every top-level child, not just the first") + assert.Contains(t, got, "Foo") + assert.Contains(t, got, "Bar") +} + +func TestBuildRefParamsHeader_EmptyInputReturnsEmpty(t *testing.T) { + got, err := buildRefParamsHeader("") + require.NoError(t, err) + assert.Empty(t, got) +} + +func TestUnsubscribePullPoint_EchoesRefParamsWithIsReferenceParameter(t *testing.T) { + ref := subscriptionRef{ + Address: "http://192.168.1.10/onvif/services", + RefParamsXML: `297`, + } + fc := newFakeCaller() + require.NoError(t, unsubscribePullPoint(fc, ref)) + require.Len(t, fc.sendSoapHeaders, 1) + hdr := fc.sendSoapHeaders[0] + assert.Contains(t, hdr, "SubscriptionId") + assert.Contains(t, hdr, `IsReferenceParameter="true"`) +} + +func TestUnsubscribePullPoint_EmptyAddressIsNoOp(t *testing.T) { + fc := newFakeCaller() + require.NoError(t, unsubscribePullPoint(fc, subscriptionRef{})) + assert.Empty(t, fc.sendSoapCalls, "no SOAP call should happen when there is no subscription endpoint") +} diff --git a/event/stream/stream.go b/event/stream/stream.go index 9e39f06..ab0d9ef 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -110,6 +110,17 @@ func (o Options) withDefaults() Options { return d } +// subscriptionRef holds the result of CreatePullPointSubscription. +// AXIS encodes the subscription identity in RefParamsXML (a generic +// /onvif/services Address plus a child); +// other vendors put the identity in the Address itself, leaving +// RefParamsXML empty. Subscription-scoped requests must echo a +// non-empty RefParamsXML — see extractReferenceParameters. +type subscriptionRef struct { + Address string + RefParamsXML string +} + // caller is the *onvif.Device subset Stream depends on. Implementations // must: // @@ -125,6 +136,7 @@ func (o Options) withDefaults() Options { type caller interface { CallMethod(method any) (*http.Response, error) SendSoap(endpoint, body string) (*http.Response, error) + SendSoapWithHeader(endpoint, body, headerXML string) (*http.Response, error) } type deviceCaller struct{ dev *onvif.Device } @@ -137,6 +149,10 @@ func (d deviceCaller) SendSoap(endpoint, body string) (*http.Response, error) { return d.dev.SendSoap(endpoint, body) } +func (d deviceCaller) SendSoapWithHeader(endpoint, body, headerXML string) (*http.Response, error) { + return d.dev.SendSoapWithHeader(endpoint, body, headerXML) +} + // Stream owns a single ONVIF pull-point subscription. Safe for Close // from any goroutine while readers consume Events / Errors. Close is // idempotent. @@ -145,7 +161,7 @@ type Stream struct { opts Options pullPointMu sync.Mutex - pullPoint string + pullPoint subscriptionRef events chan Event errors chan error @@ -160,16 +176,16 @@ type Stream struct { now func() time.Time } -func (s *Stream) getPullPoint() string { +func (s *Stream) getPullPoint() subscriptionRef { s.pullPointMu.Lock() defer s.pullPointMu.Unlock() return s.pullPoint } -func (s *Stream) setPullPoint(addr string) { +func (s *Stream) setPullPoint(ref subscriptionRef) { s.pullPointMu.Lock() defer s.pullPointMu.Unlock() - s.pullPoint = addr + s.pullPoint = ref } // NewStream creates a Stream and performs CreatePullPointSubscription @@ -183,7 +199,7 @@ func NewStream(ctx context.Context, dev *onvif.Device, opts Options) (*Stream, e func newStream(ctx context.Context, c caller, opts Options) (*Stream, error) { opts = opts.withDefaults() - addr, err := createPullPoint(c, opts) + ref, err := createPullPoint(c, opts) if err != nil { return nil, fmt.Errorf("create pull point subscription: %w", err) } @@ -191,7 +207,7 @@ func newStream(ctx context.Context, c caller, opts Options) (*Stream, error) { s := &Stream{ caller: c, opts: opts, - pullPoint: addr, + pullPoint: ref, events: make(chan Event, opts.BufferSize), errors: make(chan error, opts.BufferSize), cancel: cancel, diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index f58aa0c..0538aba 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -33,6 +33,7 @@ type fakeCaller struct { defaultCall fakeResp callMethodCalls []any sendSoapCalls [][2]string + sendSoapHeaders []string blockUnsubscribe chan struct{} blockAllSendSoap chan struct{} } @@ -104,6 +105,16 @@ func (f *fakeCaller) SendSoap(endpoint, body string) (*http.Response, error) { return &http.Response{Body: io.NopCloser(strings.NewReader(r.body))}, r.err } +// SendSoapWithHeader delegates body+endpoint recording to SendSoap so +// existing assertions on sendSoapCalls keep working, and records the +// header XML in a parallel slice for ref-params wiring tests. +func (f *fakeCaller) SendSoapWithHeader(endpoint, body, headerXML string) (*http.Response, error) { + f.mu.Lock() + f.sendSoapHeaders = append(f.sendSoapHeaders, headerXML) + f.mu.Unlock() + return f.SendSoap(endpoint, body) +} + func (f *fakeCaller) sendSoapCallCount() int { f.mu.Lock() defer f.mu.Unlock() From d726ed8edb311ba58018a2992ad404436c154ecc Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:56 +0200 Subject: [PATCH 49/53] fix(event/stream): address review findings on AXIS compat work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical - Device.SendSoapWithHeader now parses the supplied header content with etree and adds each top-level child as its own SOAP Header block. gosoap.AddStringHeaderContent only accepts a single root element; previously a multi-child ref-params header silently produced a header-less request because the parse error was discarded. Errors are now propagated. - enrichSOAPErr scrubs <*:Security> blocks from response bodies before fault extraction or excerpt slicing so a camera that echoes the WS-Security header in a fault response cannot leak Username/Password into operator logs. Important - extractSOAPFault falls back to the SOAP 1.2 Subcode (e.g. ter:InvalidArgs) when Reason/Text is empty — consistent with enrichSOAPErr and surfaces actionable detail on 200-OK fault bodies reached via unmarshalNode. - subscriptionRef captures the camera-granted TerminationTime from CreatePullPointSubscription and Renew responses. renewLoop schedules from it via the new nextRenewInterval helper so we never miss a renew when the camera grants less than requested. renew is now a sleep-loop driven by the latest granted time. - enrichSOAPErr reads at most 64 KiB from the body (vs. 10 MiB on success paths). Fault bodies are always small; the prior cap let a wedged camera churn 10 MiB/s through the retry loop. Suggestions - extractReferenceParameters anchors to so a wsa:ReplyTo / wsa:FaultTo that also carries ReferenceParameters elsewhere in the envelope cannot leak through and break PullMessages. - buildRefParamsHeader accepts either raw children or the full <*:ReferenceParameters> wrapper, and propagates ancestor xmlns:* onto each child so a vendor that declares the prefix on the parent (not the child itself, as AXIS does) still produces valid standalone children on the wire. - SendSoapWithHeader documents that xmlHeaderContent must be well-formed XML and that the caller is responsible for escaping any externally sourced data. - Error-message ordering is now context-first ("SOAP fault: X: ") per Go convention. - Dead headerEnd slicing removed from the SendSoapWithHeader test. Tests - End-to-end multi-child wiring through pullMessages. - Digest auth retry preserves the injected header. - Malformed-XML header content fast-fails before any request. - buildRefParamsHeader malformed / whitespace-only edge cases. - goleak.VerifyTestMain in event/stream catches any pull/renew goroutine that outlives its Stream. No new behavioural surface added to onvif core; SendSoap retains its signature, SendSoapWithHeader is the only new public method. --- Device.go | 42 ++++++- Device_test.go | 89 +++++++++++++- event/stream/main_test.go | 14 +++ event/stream/reconnect_test.go | 2 - event/stream/renew.go | 74 +++++++----- event/stream/renew_test.go | 5 +- event/stream/soap.go | 131 ++++++++++++++++----- event/stream/soap_test.go | 209 +++++++++++++++++++++++++++++++++ event/stream/stream.go | 15 ++- event/stream/stream_test.go | 2 - go.mod | 1 + go.sum | 2 + 12 files changed, 514 insertions(+), 72 deletions(-) create mode 100644 event/stream/main_test.go diff --git a/Device.go b/Device.go index a9749be..a4d2a1c 100644 --- a/Device.go +++ b/Device.go @@ -333,7 +333,7 @@ func (dev *Device) GetEndpointByRequestStruct(requestStruct interface{}) (string return resp, err }*/ -// CallMethod functions call an method, defined struct with authentication data +// SendSoap POSTs the given body wrapped in a SOAP envelope. func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Response, error) { return dev.SendSoapWithHeader(endpoint, xmlRequestBody, "") } @@ -342,13 +342,20 @@ func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Respon // needed to echo WS-Addressing ReferenceParameters (with // wsa:IsReferenceParameter="true") back to vendors like AXIS that // identify pull-point subscriptions through them rather than the URL. +// +// xmlHeaderContent must be well-formed XML representing zero or more +// SOAP Header child elements (sibling top-level elements are +// supported; the spec lets each reference parameter be its own header +// block). The caller is responsible for escaping any externally +// sourced data inside it. Malformed XML returns an error before any +// request is made. func (dev Device) SendSoapWithHeader(endpoint, xmlRequestBody, xmlHeaderContent string) (*http.Response, error) { soap := gosoap.NewEmptySOAP() soap.AddStringBodyContent(xmlRequestBody) soap.AddRootNamespaces(Xlmns) soap.AddAction() - if xmlHeaderContent != "" { - _ = soap.AddStringHeaderContent(xmlHeaderContent) + if err := addHeaderChildren(&soap, xmlHeaderContent); err != nil { + return nil, err } if dev.params.Username != "" && dev.params.Password != "" { soap.AddWSSecurity(dev.params.Username, dev.params.Password) @@ -364,6 +371,35 @@ func (dev Device) SendSoapWithHeader(endpoint, xmlRequestBody, xmlHeaderContent return servResp, err } +// addHeaderChildren wraps the fragment so etree can parse multi-root +// XML, then adds each top-level child as its own SOAP Header block. +// gosoap.AddStringHeaderContent only accepts a single root element. +func addHeaderChildren(soap *gosoap.SoapMessage, xmlHeaderContent string) error { + if xmlHeaderContent == "" { + return nil + } + doc := etree.NewDocument() + if err := doc.ReadFromString("" + xmlHeaderContent + ""); err != nil { + return fmt.Errorf("parse header content: %w", err) + } + wrap := doc.SelectElement("wrap") + if wrap == nil { + return errors.New("parse header content: missing wrap root") + } + for _, child := range wrap.ChildElements() { + d := etree.NewDocument() + d.SetRoot(child.Copy()) + s, err := d.WriteToString() + if err != nil { + return fmt.Errorf("serialise header child: %w", err) + } + if err := soap.AddStringHeaderContent(s); err != nil { + return fmt.Errorf("add header child: %w", err) + } + } + return nil +} + func createHttpRequest(httpMethod string, endpoint string, soap string) (req *http.Request, err error) { req, err = http.NewRequest(httpMethod, endpoint, bytes.NewBufferString(soap)) if err != nil { diff --git a/Device_test.go b/Device_test.go index 8577e85..b6c2d5a 100644 --- a/Device_test.go +++ b/Device_test.go @@ -58,16 +58,97 @@ func TestDevice_SendSoapWithHeader_InjectsHeaderXML(t *testing.T) { } headerStart := strings.Index(captured, "Header>") - headerEnd := strings.Index(captured, "") require.NotEqual(t, -1, headerStart, "envelope must contain
; got: %s", captured) - assert.Greater(t, headerEnd, headerStart, "envelope must close the header") + require.Greater(t, bodyStart, headerStart, "Body must follow Header in the envelope") - headerSlice := captured[headerStart:strings.Index(captured, "Body>")] + headerSlice := captured[headerStart:bodyStart] assert.Contains(t, headerSlice, "SubscriptionId", "injected header element must land inside SOAP
") assert.Contains(t, headerSlice, "297") - bodySlice := captured[strings.Index(captured, "Body>"):] + bodySlice := captured[bodyStart:] assert.Contains(t, bodySlice, "PullMessages", "body content must land inside SOAP ") } + +// Per WS-Addressing 1.0 §3.1 every reference parameter is a separate +// SOAP Header block. Vendors that declare two ref params would silently +// produce a header-less request if the implementation only accepts a +// single top-level element. +func TestDevice_SendSoapWithHeader_AcceptsMultipleTopLevelChildren(t *testing.T) { + const headerXML = `12` + const bodyXML = `` + + var captured string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + captured = string(b) + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + + dev := Device{params: DeviceParams{ + Xaddr: strings.TrimPrefix(srv.URL, "http://"), + HttpClient: srv.Client(), + }} + resp, err := dev.SendSoapWithHeader(srv.URL, bodyXML, headerXML) + require.NoError(t, err) + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + + headerSlice := captured[strings.Index(captured, "Header>"):strings.Index(captured, "Body>")] + assert.Contains(t, headerSlice, "Foo") + assert.Contains(t, headerSlice, "Bar") +} + +// Digest auth fallback path: the camera 401s the first POST and the +// retry computes a digest. The ref-params header must survive the +// retry — losing it would silently re-introduce the AXIS regression +// on every authenticated camera. +func TestDevice_SendSoapWithHeader_PreservesHeaderAcrossDigestRetry(t *testing.T) { + const headerXML = `297` + var capturedSecondBody string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Authorization") == "" { + w.Header().Set("WWW-Authenticate", `Digest realm="onvif", nonce="abc", qop="auth"`) + w.WriteHeader(http.StatusUnauthorized) + return + } + b, _ := io.ReadAll(r.Body) + capturedSecondBody = string(b) + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + + dev := Device{params: DeviceParams{ + Xaddr: strings.TrimPrefix(srv.URL, "http://"), + HttpClient: srv.Client(), + Username: "admin", + Password: "secret", + }} + resp, err := dev.SendSoapWithHeader(srv.URL, ``, headerXML) + require.NoError(t, err) + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + assert.Contains(t, capturedSecondBody, "SubscriptionId", + "digest retry must carry the same ref-params header as the first attempt") + assert.Contains(t, capturedSecondBody, "297") +} + +func TestDevice_SendSoapWithHeader_PropagatesMalformedHeaderError(t *testing.T) { + var hits int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hits++ + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + dev := Device{params: DeviceParams{HttpClient: srv.Client()}} + + _, err := dev.SendSoapWithHeader(srv.URL, "", " http://camera.local/onvif/Events/PullSub_2 - 2026-05-21T10:30:10Z - 2026-05-21T10:31:10Z ` diff --git a/event/stream/renew.go b/event/stream/renew.go index ef0280d..e8cee2f 100644 --- a/event/stream/renew.go +++ b/event/stream/renew.go @@ -10,53 +10,71 @@ import ( "github.com/kerberos-io/onvif/xsd" ) -// renewLoop surfaces renew failures and continues. A permanently -// failing renew lets the subscription die at the camera; the pull -// loop's reconnect path then recreates it — recreate is the only -// reliable recovery once a subscription is GC'd. +// renewLoop sleeps until the next deadline (camera-granted termination +// minus RenewMargin), renews, and repeats. A permanently failing +// renew lets the subscription die at the camera; the pull loop's +// reconnect path then recreates it — recreate is the only reliable +// recovery once a subscription is GC'd. func (s *Stream) renewLoop(ctx context.Context) { - interval := s.opts.InitialTermination - s.opts.RenewMargin - if interval <= 0 { - // Pathological config (margin >= termination): renew at - // half termination so we still refresh. - interval = s.opts.InitialTermination / 2 - if interval <= 0 { - interval = time.Second - } - } - ticker := time.NewTicker(interval) - defer ticker.Stop() for { - select { - case <-ctx.Done(): + ref := s.getPullPoint() + if !sleepCtx(ctx, nextRenewInterval(ref.GrantedTermination, s.opts, time.Now())) { return - case <-ticker.C: - if err := renewPullPoint(s.caller, s.getPullPoint(), s.opts); err != nil { - s.surfaceError(ErrRenewFailed{Err: err}) - } + } + granted, err := renewPullPoint(s.caller, s.getPullPoint(), s.opts) + if err != nil { + s.surfaceError(ErrRenewFailed{Err: err}) + continue + } + if !granted.IsZero() { + s.updateGrantedTermination(granted) } } } +// nextRenewInterval prefers the camera-granted termination so we never +// schedule a renew past the actual expiry, with opts.InitialTermination +// as the fallback when the camera didn't supply one. +func nextRenewInterval(granted time.Time, opts Options, now time.Time) time.Duration { + var base time.Duration + if !granted.IsZero() { + base = granted.Sub(now) + } else { + base = opts.InitialTermination + } + d := base - opts.RenewMargin + if d <= 0 { + d = base / 2 + } + if d <= 0 { + d = time.Second + } + return d +} + // renewPullPoint sends Renew with an absolute UTC TerminationTime. // WS-BaseNotification §6.1.1 also allows xsd:duration but older // Hikvision, some Dahua and some Bosch firmwares reject the -// relative form. -func renewPullPoint(c caller, ref subscriptionRef, opts Options) error { +// relative form. Returns the camera-granted TerminationTime parsed +// from the response (zero on absence) so the caller can reschedule. +func renewPullPoint(c caller, ref subscriptionRef, opts Options) (time.Time, error) { absoluteEnd := time.Now().UTC().Add(opts.InitialTermination).Format("2006-01-02T15:04:05Z") req := event.Renew{TerminationTime: xsd.String(absoluteEnd)} body, err := xml.Marshal(req) if err != nil { - return fmt.Errorf("marshal Renew: %w", err) + return time.Time{}, fmt.Errorf("marshal Renew: %w", err) } headerXML, err := buildRefParamsHeader(ref.RefParamsXML) if err != nil { - return fmt.Errorf("build ref params header: %w", err) + return time.Time{}, fmt.Errorf("build ref params header: %w", err) } resp, err := c.SendSoapWithHeader(ref.Address, string(body), headerXML) if err != nil { - return enrichSOAPErr(resp, err) + return time.Time{}, enrichSOAPErr(resp, err) } - _, err = readClose(resp) - return err + respBody, err := readClose(resp) + if err != nil { + return time.Time{}, err + } + return extractTerminationTime(respBody), nil } diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go index f9c0a12..55acf55 100644 --- a/event/stream/renew_test.go +++ b/event/stream/renew_test.go @@ -175,7 +175,7 @@ func TestRenew_SendsAbsoluteDateTimeNotDuration(t *testing.T) { func TestRenewPullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) { fc := newFakeCaller() fc.queueSendSoap(renewFaultBody, errors.New("400 Bad Request")) - err := renewPullPoint(fc, subscriptionRef{Address: "http://camera/sub"}, defaultOptions()) + _, err := renewPullPoint(fc, subscriptionRef{Address: "http://camera/sub"}, defaultOptions()) require.Error(t, err) assert.Contains(t, err.Error(), "renew-specific complaint", "renewPullPoint must enrich transport errors with the camera's SOAP fault") @@ -194,7 +194,8 @@ func TestRenewPullPoint_EchoesRefParamsWithIsReferenceParameter(t *testing.T) { RefParamsXML: `297`, } fc := newFakeCaller() - require.NoError(t, renewPullPoint(fc, ref, defaultOptions())) + _, err := renewPullPoint(fc, ref, defaultOptions()) + require.NoError(t, err) require.Len(t, fc.sendSoapHeaders, 1) hdr := fc.sendSoapHeaders[0] assert.Contains(t, hdr, "SubscriptionId") diff --git a/event/stream/soap.go b/event/stream/soap.go index 17ab885..489882c 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -17,12 +17,18 @@ import ( "github.com/kerberos-io/onvif/xsd" ) -// maxResponseBytes caps SOAP response buffering. ONVIF PullMessages -// bodies are normally <100KB even with dense analytics payloads; -// 10 MiB is comfortably above legitimate traffic while keeping a -// hostile or buggy camera from OOMing the process. +// maxResponseBytes caps SOAP response buffering on success paths. +// ONVIF PullMessages bodies are normally <100KB even with dense +// analytics payloads; 10 MiB is well above legitimate traffic while +// keeping a hostile or buggy camera from OOMing the process. const maxResponseBytes = 10 << 20 +// maxErrorBodyBytes caps the body read by enrichSOAPErr. The pull +// retry loop runs every RetryBackoff (~1s) so an unbounded read on +// the error path would churn 10 MiB/s per wedged camera. Fault bodies +// are always small. +const maxErrorBodyBytes = 64 << 10 + func createPullPoint(c caller, opts Options) (subscriptionRef, error) { term := xsd.String(durationToXSD(opts.InitialTermination)) req := event.CreatePullPointSubscription{InitialTerminationTime: &term} @@ -50,9 +56,30 @@ func createPullPoint(c caller, opts Options) (subscriptionRef, error) { if addr == "" { return subscriptionRef{}, errors.New("CreatePullPointSubscription response has empty SubscriptionReference Address") } - return subscriptionRef{Address: addr, RefParamsXML: extractReferenceParameters(body)}, nil + return subscriptionRef{ + Address: addr, + RefParamsXML: extractReferenceParameters(body), + GrantedTermination: extractTerminationTime(body), + }, nil } +// extractTerminationTime parses the absolute UTC instant the camera +// granted as the subscription expiry. Returns zero on absence or parse +// failure — callers fall back to opts.InitialTermination. +func extractTerminationTime(body string) time.Time { + m := terminationTimeRE.FindStringSubmatch(body) + if len(m) < 2 { + return time.Time{} + } + t, err := time.Parse(time.RFC3339, strings.TrimSpace(m[1])) + if err != nil { + return time.Time{} + } + return t +} + +var terminationTimeRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?TerminationTime\b[^>]*>(.*?)\s]+:)?TerminationTime>`) + // pullMessages returns an empty slice (no error) when the camera had // nothing within PullTimeout. func pullMessages(c caller, ref subscriptionRef, opts Options) ([]event.NotificationMessage, error) { @@ -159,31 +186,47 @@ var ( soap12FaultRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Reason\b[^>]*>.*?<(?:[^:>\s]+:)?Text[^>]*>(.*?)\s]+:)?Text>`) // SOAP 1.2 Subcode: ...ter:InvalidArgs... soap12SubcodeRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Subcode\b[^>]*>.*?<(?:[^:>\s]+:)?Value[^>]*>(.*?)\s]+:)?Value>`) + + // WS-Security blocks may carry our Username/Password if the camera + // echoes the request in a fault; scrub before logging. + wsseSecurityRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Security\b[^>]*>.*?\s]+:)?Security>`) ) -// extractSOAPFault returns the reason text from a SOAP fault or empty -// when the body is not a fault. Handles SOAP 1.1 (faultstring) and -// SOAP 1.2 (Reason/Text) shapes. +// extractSOAPFault returns the reason text from a SOAP fault, falling +// back to the Subcode value when Reason/Text is empty (AXIS pattern). +// Returns "" when the body is not a fault. func extractSOAPFault(body string) string { if !strings.Contains(body, "Fault") { return "" } if m := soap11FaultRE.FindStringSubmatch(body); len(m) > 1 { - return strings.TrimSpace(m[1]) + if r := strings.TrimSpace(m[1]); r != "" { + return r + } } if m := soap12FaultRE.FindStringSubmatch(body); len(m) > 1 { - return strings.TrimSpace(m[1]) + if r := strings.TrimSpace(m[1]); r != "" { + return r + } } - return "" + return extractSOAPSubcode(body) } -var refParamsRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?ReferenceParameters\b[^>]*>(.*?)\s]+:)?ReferenceParameters>`) +// Anchored to SubscriptionReference because other WS-Addressing +// endpoint references in the same envelope (wsa:ReplyTo, wsa:FaultTo, +// wsa:From) may also carry ReferenceParameters that are not ours. +var ( + subscriptionRefRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?SubscriptionReference\b[^>]*>(.*?)\s]+:)?SubscriptionReference>`) + refParamsRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?ReferenceParameters\b[^>]*>(.*?)\s]+:)?ReferenceParameters>`) +) // buildRefParamsHeader produces the SOAP
inner XML for a set -// of WS-Addressing ReferenceParameters: each top-level child element -// is re-emitted with wsa:IsReferenceParameter="true" added, as the -// spec requires. Empty input yields empty output (no-op for vendors -// that encode subscription identity in the URL). +// of WS-Addressing ReferenceParameters: each ref-param element is +// re-emitted with wsa:IsReferenceParameter="true" and any xmlns:* +// it inherited from the parent element. Input +// may be either the raw children or the full <*:ReferenceParameters> +// wrapper — extractReferenceParameters returns the wrapper so parent- +// scoped namespace declarations survive into the rebuild. func buildRefParamsHeader(rawXML string) (string, error) { if strings.TrimSpace(rawXML) == "" { return "", nil @@ -196,11 +239,23 @@ func buildRefParamsHeader(rawXML string) (string, error) { if wrap == nil { return "", errors.New("parse ref params: missing wrap root") } + + children := wrap.ChildElements() + var ambient *etree.Element + if len(children) == 1 && strings.HasSuffix(children[0].Tag, "ReferenceParameters") { + ambient = children[0] + children = ambient.ChildElements() + } + var out strings.Builder - for _, child := range wrap.ChildElements() { - child.CreateAttr("wsa:IsReferenceParameter", "true") + for _, child := range children { + c := child.Copy() + if ambient != nil { + inheritXmlns(c, ambient) + } + c.CreateAttr("wsa:IsReferenceParameter", "true") d := etree.NewDocument() - d.SetRoot(child.Copy()) + d.SetRoot(c) s, err := d.WriteToString() if err != nil { return "", fmt.Errorf("serialise ref param child: %w", err) @@ -210,16 +265,37 @@ func buildRefParamsHeader(rawXML string) (string, error) { return out.String(), nil } +// inheritXmlns copies xmlns / xmlns:* declarations from src onto dst +// when dst doesn't already declare them, so a child whose namespace +// prefix was declared on an ancestor stays valid in isolation. +func inheritXmlns(dst, src *etree.Element) { + for _, attr := range src.Attr { + isDefault := attr.Space == "" && attr.Key == "xmlns" + isPrefixed := attr.Space == "xmlns" + if !isDefault && !isPrefixed { + continue + } + key := attr.Key + if isPrefixed { + key = "xmlns:" + attr.Key + } + if dst.SelectAttr(key) != nil { + continue + } + dst.CreateAttr(key, attr.Value) + } +} + // extractReferenceParameters returns the verbatim inner XML so callers // can echo it (with wsa:IsReferenceParameter="true") into the SOAP // Header of subscription-scoped requests per WS-Addressing 1.0 §3.1. // Without that echo, AXIS rejects PullMessages with ter:InvalidArgs. func extractReferenceParameters(body string) string { - m := refParamsRE.FindStringSubmatch(body) - if len(m) < 2 { + sub := subscriptionRefRE.FindStringSubmatch(body) + if len(sub) < 2 { return "" } - return strings.TrimSpace(m[1]) + return strings.TrimSpace(refParamsRE.FindString(sub[1])) } // extractSOAPSubcode is the fallback when Reason/Text is empty — AXIS @@ -251,22 +327,19 @@ func enrichSOAPErr(resp *http.Response, err error) error { return err } defer resp.Body.Close() - b, readErr := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes)) + b, readErr := io.ReadAll(io.LimitReader(resp.Body, maxErrorBodyBytes)) if readErr != nil || len(b) == 0 { return err } - body := string(b) + body := wsseSecurityRE.ReplaceAllString(string(b), "[REDACTED]") if reason := extractSOAPFault(body); reason != "" { - return fmt.Errorf("%w: SOAP fault: %s", err, reason) - } - if sub := extractSOAPSubcode(body); sub != "" { - return fmt.Errorf("%w: SOAP fault subcode: %s", err, sub) + return fmt.Errorf("SOAP fault: %s: %w", reason, err) } excerpt := strings.TrimSpace(body) if len(excerpt) > maxErrExcerpt { excerpt = excerpt[:maxErrExcerpt] + "...(truncated)" } - return fmt.Errorf("%w: response body: %s", err, excerpt) + return fmt.Errorf("response body: %s: %w", excerpt, err) } // durationToXSD formats a duration as xsd:duration PTnS. Second diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index 9e2b954..20edd4d 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -7,6 +7,7 @@ import ( "net/http" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -392,3 +393,211 @@ func TestUnsubscribePullPoint_EmptyAddressIsNoOp(t *testing.T) { require.NoError(t, unsubscribePullPoint(fc, subscriptionRef{})) assert.Empty(t, fc.sendSoapCalls, "no SOAP call should happen when there is no subscription endpoint") } + +// End-to-end multi-child wiring through the production caller, not +// just the unit-tested builder. Without the fix to addHeaderChildren +// in Device.SendSoapWithHeader, the second child would silently +// vanish from the wire envelope. +func TestPullMessages_TwoRefParamsEachLandsOnTheWire(t *testing.T) { + ref := subscriptionRef{ + Address: "http://camera/sub", + RefParamsXML: `1` + + `2`, + } + fc := newFakeCaller() + _, err := pullMessages(fc, ref, defaultOptions()) + require.NoError(t, err) + require.Len(t, fc.sendSoapHeaders, 1) + hdr := fc.sendSoapHeaders[0] + assert.Equal(t, 2, strings.Count(hdr, `IsReferenceParameter="true"`)) + assert.Contains(t, hdr, "Foo") + assert.Contains(t, hdr, "Bar") +} + +// A camera echoing our request in a fault response (some debug-mode +// firmwares do) or a fault that includes the Security header verbatim +// would otherwise leak the WS-Security Username/Password into operator +// logs. The body excerpt must scrub the Security block before the +// fault extractor and the excerpt fallback see it. +func TestEnrichSOAPErr_RedactsWSSESecurityBlock(t *testing.T) { + body := ` + + admin + hunter2 + + plain text excerpt +` + got := enrichSOAPErr(fakeResponse(body), errors.New("400")) + require.Error(t, got) + assert.NotContains(t, got.Error(), "hunter2", "Password must never reach logs") + assert.NotContains(t, got.Error(), "admin", "Username must never reach logs") + assert.Contains(t, got.Error(), "REDACTED", "redaction marker must remain visible") +} + +// Same vendor pattern as the enrichSOAPErr case but reached via +// unmarshalNode → extractSOAPFault on a 200 OK response carrying a +// Fault. Diverging from enrichSOAPErr's fallback chain would mean +// PullMessages reports "missing PullMessagesResponse element" instead +// of the actionable ter:InvalidArgs. +func TestExtractSOAPFault_FallsBackToSubcodeWhenReasonEmpty(t *testing.T) { + body := ` + + + env:Sender + ter:InvalidArgs + + + +` + assert.Equal(t, "ter:InvalidArgs", extractSOAPFault(body)) +} + +// WS-Addressing §3.1 allows ReferenceParameters in any endpoint +// reference (wsa:From, wsa:ReplyTo, wsa:FaultTo, ...). An unanchored +// search would silently pick up the wrong one. +func TestExtractReferenceParameters_AnchoredToSubscriptionReference(t *testing.T) { + body := ` + + + http://anon + + DO-NOT-PICK + + + + + + http://camera/sub + + 297 + + + +` + got := extractReferenceParameters(body) + assert.Contains(t, got, "SubscriptionId") + assert.Contains(t, got, "297") + assert.NotContains(t, got, "DO-NOT-PICK", + "ref params from wsa:ReplyTo must not leak through — only SubscriptionReference's children belong on PullMessages") +} + +// When a vendor declares the namespace prefix on the parent +// element rather than the child (legal XML, just +// different from AXIS's shape), naïve inner-only extraction strips the +// declaration and produces children with orphaned prefixes that fail +// to round-trip. Inheritance must propagate ancestor xmlns onto each +// child before serialisation. +func TestBuildRefParamsHeader_InheritsParentXmlns(t *testing.T) { + parentScopedXmlns := `` + + `297` + + `` + got, err := buildRefParamsHeader(parentScopedXmlns) + require.NoError(t, err) + assert.NotContains(t, got, "ReferenceParameters", + "the wrapping element must not appear in output — each param child is its own header block") + assert.Contains(t, got, "SubscriptionId") + assert.Contains(t, got, "297") + assert.Contains(t, got, `xmlns:dom0="urn:vendor:axis"`, + "the dom0 prefix is undeclared on the child itself — it must be inherited from the parent so the standalone child stays valid XML") + assert.Contains(t, got, `IsReferenceParameter="true"`) +} + +// Pins the contract change: extractReferenceParameters returns the +// full element (including its own attributes), +// not just the inner content, so parent-scoped xmlns survives into +// buildRefParamsHeader. +func TestExtractReferenceParameters_IncludesParentElementForXmlnsPreservation(t *testing.T) { + body := ` + + + http://camera + + 297 + + + +` + got := extractReferenceParameters(body) + assert.Contains(t, got, "ReferenceParameters", + "extractor must include the wrapping element so parent-scoped xmlns survives") + assert.Contains(t, got, `xmlns:dom0="urn:vendor:axis"`) + assert.Contains(t, got, "SubscriptionId") +} + +// --- Camera-granted TerminationTime ----------------------------------- +// +// Cameras may grant a shorter subscription than we ask for. Scheduling +// the next renew from opts.InitialTermination instead of what the +// camera actually granted leads to expired subscriptions and the +// recreate-recovery path firing unnecessarily. + +func TestCreatePullPoint_CapturesGrantedTermination(t *testing.T) { + body := ` + + http://camera/sub + 2026-05-27T13:19:11Z + 2026-05-27T13:21:11Z + +` + fc := newFakeCaller() + fc.queueCallMethod(body, nil) + ref, err := createPullPoint(fc, defaultOptions()) + require.NoError(t, err) + expected, _ := time.Parse(time.RFC3339, "2026-05-27T13:21:11Z") + assert.Equal(t, expected, ref.GrantedTermination) +} + +func TestCreatePullPoint_NoTerminationTimeYieldsZeroTime(t *testing.T) { + body := ` + + http://camera/sub + +` + fc := newFakeCaller() + fc.queueCallMethod(body, nil) + ref, err := createPullPoint(fc, defaultOptions()) + require.NoError(t, err) + assert.True(t, ref.GrantedTermination.IsZero(), + "absent TerminationTime must yield zero so renew falls back to opts") +} + +func TestNextRenewInterval_UsesGrantedTerminationMinusMargin(t *testing.T) { + now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + granted := now.Add(60 * time.Second) + opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second} + assert.Equal(t, 50*time.Second, nextRenewInterval(granted, opts, now)) +} + +func TestNextRenewInterval_FallsBackToInitialTerminationWhenGrantedZero(t *testing.T) { + now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second} + assert.Equal(t, 50*time.Second, nextRenewInterval(time.Time{}, opts, now)) +} + +func TestNextRenewInterval_FloorsAtOneSecondIfAlreadyExpired(t *testing.T) { + now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + granted := now.Add(-1 * time.Second) // camera says we're already expired + opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second} + assert.Equal(t, time.Second, nextRenewInterval(granted, opts, now), + "never sleep zero or negative — recreate-recovery handles the truly-dead case") +} + +func TestBuildRefParamsHeader_MalformedXMLReturnsError(t *testing.T) { + _, err := buildRefParamsHeader(" http://camera.local/onvif/Events/PullSub_1 - 2026-05-21T10:30:00Z - 2026-05-21T10:31:00Z ` diff --git a/go.mod b/go.mod index f98d94f..5554aaa 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + go.uber.org/goleak v1.3.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.16.0 // indirect golang.org/x/sys v0.15.0 // indirect diff --git a/go.sum b/go.sum index b3fac64..5211fc0 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= From c7ef445d6ad56e4b0225155ff703789bcc5700b9 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:56 +0200 Subject: [PATCH 50/53] refactor(onvif): add SendSoapWithOptions as the variadic workhorse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Future per-call knobs (timeout, context, custom namespaces) will arrive sooner than later. Adding them as SendSoapWithHeader2 / 3 / N would turn the Device API into a combinatorial mess; adding them as new required positional args breaks every existing consumer. SendSoapWithOptions accepts variadic SoapOption values. SendSoap and SendSoapWithHeader become one-line delegates so all current callers keep their signatures, and the public surface is purely additive. Only WithHeader ships today — wiring the AXIS ReferenceParameters path through the same plumbing. New options land as WithX constructors in this file rather than as new Device methods. --- Device.go | 31 +++++++++++++++++++++++++++++-- Device_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/Device.go b/Device.go index a4d2a1c..c79d43b 100644 --- a/Device.go +++ b/Device.go @@ -335,7 +335,7 @@ func (dev *Device) GetEndpointByRequestStruct(requestStruct interface{}) (string // SendSoap POSTs the given body wrapped in a SOAP envelope. func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Response, error) { - return dev.SendSoapWithHeader(endpoint, xmlRequestBody, "") + return dev.SendSoapWithOptions(endpoint, xmlRequestBody) } // SendSoapWithHeader is SendSoap plus arbitrary inner-Header XML — @@ -350,11 +350,38 @@ func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Respon // sourced data inside it. Malformed XML returns an error before any // request is made. func (dev Device) SendSoapWithHeader(endpoint, xmlRequestBody, xmlHeaderContent string) (*http.Response, error) { + return dev.SendSoapWithOptions(endpoint, xmlRequestBody, WithHeader(xmlHeaderContent)) +} + +// SoapOption tweaks a single SendSoapWithOptions call. New options +// (per-call timeout, context, custom envelope namespaces, ...) should +// be added as WithX constructors here rather than as new method +// variants on Device. +type SoapOption func(*soapConfig) + +type soapConfig struct { + headerContent string +} + +// WithHeader adds inner-Header XML to the envelope. See +// SendSoapWithHeader for the content contract. +func WithHeader(xml string) SoapOption { + return func(c *soapConfig) { c.headerContent = xml } +} + +// SendSoapWithOptions is the workhorse behind SendSoap and +// SendSoapWithHeader; call it directly when you need to combine +// options or pass options not surfaced by the convenience wrappers. +func (dev Device) SendSoapWithOptions(endpoint, xmlRequestBody string, opts ...SoapOption) (*http.Response, error) { + var cfg soapConfig + for _, o := range opts { + o(&cfg) + } soap := gosoap.NewEmptySOAP() soap.AddStringBodyContent(xmlRequestBody) soap.AddRootNamespaces(Xlmns) soap.AddAction() - if err := addHeaderChildren(&soap, xmlHeaderContent); err != nil { + if err := addHeaderChildren(&soap, cfg.headerContent); err != nil { return nil, err } if dev.params.Username != "" && dev.params.Password != "" { diff --git a/Device_test.go b/Device_test.go index b6c2d5a..7c6d96e 100644 --- a/Device_test.go +++ b/Device_test.go @@ -103,6 +103,49 @@ func TestDevice_SendSoapWithHeader_AcceptsMultipleTopLevelChildren(t *testing.T) assert.Contains(t, headerSlice, "Bar") } +// SendSoapWithOptions is the variadic shape that future per-call +// options (timeout, context, ...) will hang off. SendSoap and +// SendSoapWithHeader stay as thin convenience wrappers so existing +// callers are not forced to migrate. +func TestDevice_SendSoapWithOptions_WithHeaderMatchesSendSoapWithHeader(t *testing.T) { + const headerXML = `42` + const bodyXML = `` + + var captured string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + captured = string(b) + })) + t.Cleanup(srv.Close) + + dev := Device{params: DeviceParams{HttpClient: srv.Client()}} + resp, err := dev.SendSoapWithOptions(srv.URL, bodyXML, WithHeader(headerXML)) + require.NoError(t, err) + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + assert.Contains(t, captured, "SubscriptionId") + assert.Contains(t, captured, "42") +} + +func TestDevice_SendSoapWithOptions_NoOptsMatchesSendSoap(t *testing.T) { + var captured string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + captured = string(b) + })) + t.Cleanup(srv.Close) + + dev := Device{params: DeviceParams{HttpClient: srv.Client()}} + resp, err := dev.SendSoapWithOptions(srv.URL, ``) + require.NoError(t, err) + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + assert.NotContains(t, captured, "IsReferenceParameter", + "no opts should produce a header-less envelope") +} + // Digest auth fallback path: the camera 401s the first POST and the // retry computes a digest. The ref-params header must survive the // retry — losing it would silently re-introduce the AXIS regression From d7cfee56a19b766ebbc1cfa4fd3ca97148724470 Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:56 +0200 Subject: [PATCH 51/53] fix(event/stream): address round-2 review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical - Renew busy-loop on persistent failure: after a failed renewPullPoint, the loop re-read the (now in-past) GrantedTermination and nextRenewInterval floored to 1s, hammering the camera at 1 Hz until reconnect. nextRenewIntervalAfterError decouples the failure path from the stale grant and backs off at opts.RetryBackoff. renewLoop also routes through s.now() so test clocks can drive it deterministically. - Lost-update race on GrantedTermination: a renew result for an old subscription could overwrite the grant on a new one if attemptRecreate swapped pullPoint mid-flight. A generation counter on Stream tracks subscription rotation; the renew loop captures the generation before the SOAP call and discards the result if the subscription was rotated. - Credential leak when straddled the 64 KiB error cap: the non-greedy regex required a closing tag and missed the truncated case. wsseSecurityRE now matches close-tag-or-EOF. Belt-and-braces wssePasswordRE redacts elements outside any Security wrapper. - addHeaderChildren accepted well-formed-but-element-free input and produced a header-less request. Now errors out. Important - Wrapper detection in buildRefParamsHeader was HasSuffix-based and misfired on children named *ReferenceParameters. Replaced with the unambiguous wrapper-only contract: input must be the full <*:ReferenceParameters> element returned by extractReferenceParameters. - Renamed SoapOption → SendSoapOption and WithHeader → WithSOAPHeader to disambiguate at call sites. - Renamed WithHeader's `xml` parameter to headerContent to avoid shadowing the encoding/xml package name. - Documented terminationTimeRE's "first match only" semantics so nobody re-uses it from PullMessages context where multiple TerminationTime elements appear. - goleak is now a direct require (go mod tidy). Performance - Added gosoap.AddStringHeaderContents (plural) for multi-root header content. AddStringHeaderContent remains as-is for backwards compatibility with external consumers. Device.go's addHeaderChildren workaround is gone — one etree parse per SendSoapWithOptions call instead of two. Migration - Device.go's own CallOnvifFunction and the three examples now call SendSoapWithOptions, modelling the canonical path. Docs / tests - Trust-boundary warning on SendSoapWithHeader/Options godoc. - Comments on createPullPointResp/Alt explain why TerminationTime is intentionally omitted (renew timing fixtures). - New tests: digest retry strips WS-Security, duplicate WithSOAPHeader is last-wins, malformed TerminationTime yields zero, margin==base falls to base/2, empty SubscriptionReference yields empty ref params, Security straddling cap is redacted, bare Password redacted, wrapper-only contract is enforced. --- Device.go | 65 +++++++-------------- Device_test.go | 73 +++++++++++++++++++++++- event/stream/reconnect_test.go | 4 +- event/stream/renew.go | 30 ++++++++-- event/stream/renew_test.go | 55 +++++++++++++++++- event/stream/soap.go | 63 ++++++++++++--------- event/stream/soap_test.go | 91 ++++++++++++++++++++++++++++-- event/stream/stream.go | 25 +++++++- event/stream/stream_test.go | 4 ++ examples/event/pullmessage/main.go | 2 +- examples/event/renew/main.go | 2 +- examples/event/unsubscribe/main.go | 2 +- go.mod | 2 +- gosoap/soap-builder.go | 43 +++++++++++++- 14 files changed, 367 insertions(+), 94 deletions(-) diff --git a/Device.go b/Device.go index c79d43b..628d242 100644 --- a/Device.go +++ b/Device.go @@ -343,36 +343,39 @@ func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Respon // wsa:IsReferenceParameter="true") back to vendors like AXIS that // identify pull-point subscriptions through them rather than the URL. // -// xmlHeaderContent must be well-formed XML representing zero or more -// SOAP Header child elements (sibling top-level elements are -// supported; the spec lets each reference parameter be its own header -// block). The caller is responsible for escaping any externally -// sourced data inside it. Malformed XML returns an error before any -// request is made. +// xmlHeaderContent must be well-formed XML representing one or more +// SOAP Header child elements (siblings are supported; the spec lets +// each reference parameter be its own header block). Malformed or +// element-free content errors before any request is made. +// +// SECURITY: do not pass content sourced from untrusted clients. The +// API assumes the caller is authoritative for the envelope; injected +// or in the header forwards verbatim and +// may override envelope defaults under our credentials. func (dev Device) SendSoapWithHeader(endpoint, xmlRequestBody, xmlHeaderContent string) (*http.Response, error) { - return dev.SendSoapWithOptions(endpoint, xmlRequestBody, WithHeader(xmlHeaderContent)) + return dev.SendSoapWithOptions(endpoint, xmlRequestBody, WithSOAPHeader(xmlHeaderContent)) } -// SoapOption tweaks a single SendSoapWithOptions call. New options +// SendSoapOption tweaks a single SendSoapWithOptions call. New options // (per-call timeout, context, custom envelope namespaces, ...) should // be added as WithX constructors here rather than as new method // variants on Device. -type SoapOption func(*soapConfig) +type SendSoapOption func(*soapConfig) type soapConfig struct { headerContent string } -// WithHeader adds inner-Header XML to the envelope. See +// WithSOAPHeader adds inner-Header XML to the envelope. See // SendSoapWithHeader for the content contract. -func WithHeader(xml string) SoapOption { - return func(c *soapConfig) { c.headerContent = xml } +func WithSOAPHeader(headerContent string) SendSoapOption { + return func(c *soapConfig) { c.headerContent = headerContent } } // SendSoapWithOptions is the workhorse behind SendSoap and // SendSoapWithHeader; call it directly when you need to combine // options or pass options not surfaced by the convenience wrappers. -func (dev Device) SendSoapWithOptions(endpoint, xmlRequestBody string, opts ...SoapOption) (*http.Response, error) { +func (dev Device) SendSoapWithOptions(endpoint, xmlRequestBody string, opts ...SendSoapOption) (*http.Response, error) { var cfg soapConfig for _, o := range opts { o(&cfg) @@ -381,8 +384,10 @@ func (dev Device) SendSoapWithOptions(endpoint, xmlRequestBody string, opts ...S soap.AddStringBodyContent(xmlRequestBody) soap.AddRootNamespaces(Xlmns) soap.AddAction() - if err := addHeaderChildren(&soap, cfg.headerContent); err != nil { - return nil, err + if cfg.headerContent != "" { + if err := soap.AddStringHeaderContents(cfg.headerContent); err != nil { + return nil, fmt.Errorf("add header content: %w", err) + } } if dev.params.Username != "" && dev.params.Password != "" { soap.AddWSSecurity(dev.params.Username, dev.params.Password) @@ -398,34 +403,6 @@ func (dev Device) SendSoapWithOptions(endpoint, xmlRequestBody string, opts ...S return servResp, err } -// addHeaderChildren wraps the fragment so etree can parse multi-root -// XML, then adds each top-level child as its own SOAP Header block. -// gosoap.AddStringHeaderContent only accepts a single root element. -func addHeaderChildren(soap *gosoap.SoapMessage, xmlHeaderContent string) error { - if xmlHeaderContent == "" { - return nil - } - doc := etree.NewDocument() - if err := doc.ReadFromString("" + xmlHeaderContent + ""); err != nil { - return fmt.Errorf("parse header content: %w", err) - } - wrap := doc.SelectElement("wrap") - if wrap == nil { - return errors.New("parse header content: missing wrap root") - } - for _, child := range wrap.ChildElements() { - d := etree.NewDocument() - d.SetRoot(child.Copy()) - s, err := d.WriteToString() - if err != nil { - return fmt.Errorf("serialise header child: %w", err) - } - if err := soap.AddStringHeaderContent(s); err != nil { - return fmt.Errorf("add header child: %w", err) - } - } - return nil -} func createHttpRequest(httpMethod string, endpoint string, soap string) (req *http.Request, err error) { req, err = http.NewRequest(httpMethod, endpoint, bytes.NewBufferString(soap)) @@ -457,7 +434,7 @@ func (dev *Device) CallOnvifFunction(serviceName, functionName string, data []by } xmlRequestBody := string(requestBody) - servResp, err := dev.SendSoap(endpoint, xmlRequestBody) + servResp, err := dev.SendSoapWithOptions(endpoint, xmlRequestBody) if err != nil { return nil, fmt.Errorf("fail to send the '%s' request for the web service '%s', %v", functionName, serviceName, err) } diff --git a/Device_test.go b/Device_test.go index 7c6d96e..74bf502 100644 --- a/Device_test.go +++ b/Device_test.go @@ -103,6 +103,23 @@ func TestDevice_SendSoapWithHeader_AcceptsMultipleTopLevelChildren(t *testing.T) assert.Contains(t, headerSlice, "Bar") } +func TestDevice_SendSoapWithHeader_RejectsElementFreeContent(t *testing.T) { + var hits int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hits++ + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + dev := Device{params: DeviceParams{HttpClient: srv.Client()}} + + // Well-formed XML but contains no child elements — would otherwise + // parse, yield zero ChildElements, and send a header-less request. + _, err := dev.SendSoapWithHeader(srv.URL, "", "just text content") + require.Error(t, err) + assert.Equal(t, 0, hits, + "non-empty header content with no element children must fail fast") +} + // SendSoapWithOptions is the variadic shape that future per-call // options (timeout, context, ...) will hang off. SendSoap and // SendSoapWithHeader stay as thin convenience wrappers so existing @@ -119,7 +136,7 @@ func TestDevice_SendSoapWithOptions_WithHeaderMatchesSendSoapWithHeader(t *testi t.Cleanup(srv.Close) dev := Device{params: DeviceParams{HttpClient: srv.Client()}} - resp, err := dev.SendSoapWithOptions(srv.URL, bodyXML, WithHeader(headerXML)) + resp, err := dev.SendSoapWithOptions(srv.URL, bodyXML, WithSOAPHeader(headerXML)) require.NoError(t, err) if resp != nil && resp.Body != nil { resp.Body.Close() @@ -195,3 +212,57 @@ func TestDevice_SendSoapWithHeader_PropagatesMalformedHeaderError(t *testing.T) assert.Equal(t, 0, hits, "malformed header XML must fail fast — no request should reach the camera with a missing header block") } + +// Digest retry: networking.SendSoapWithDigest strips the wsse:Security +// element from the envelope before re-POSTing so credentials don't go +// on the wire twice (once via WS-Security, once via the digest header). +// Pin the behaviour from the Device layer. +func TestDevice_SendSoapWithOptions_DigestRetryStripsWSSE(t *testing.T) { + var authedBody string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Authorization") == "" { + w.Header().Set("WWW-Authenticate", `Digest realm="onvif", nonce="abc", qop="auth"`) + w.WriteHeader(http.StatusUnauthorized) + return + } + b, _ := io.ReadAll(r.Body) + authedBody = string(b) + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + + dev := Device{params: DeviceParams{ + HttpClient: srv.Client(), + Username: "admin", + Password: "secret", + }} + resp, err := dev.SendSoapWithOptions(srv.URL, "") + require.NoError(t, err) + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + require.NotEmpty(t, authedBody, "expected an authenticated POST after the 401 challenge") + assert.NotContains(t, authedBody, "UsernameToken", + "digest retry must strip wsse:Security/UsernameToken; otherwise credentials go on the wire twice") +} + +// Last-write-wins on duplicate SendSoapOption — pin the behaviour so +// the next maintainer adding an option doesn't accidentally introduce +// a merge or first-wins semantic. +func TestDevice_SendSoapWithOptions_DuplicateWithSOAPHeaderLastWins(t *testing.T) { + var captured string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + captured = string(b) + })) + t.Cleanup(srv.Close) + + dev := Device{params: DeviceParams{HttpClient: srv.Client()}} + _, err := dev.SendSoapWithOptions(srv.URL, "", + WithSOAPHeader(``), + WithSOAPHeader(``), + ) + require.NoError(t, err) + assert.NotContains(t, captured, "First", "first WithSOAPHeader must be overwritten") + assert.Contains(t, captured, "Second") +} diff --git a/event/stream/reconnect_test.go b/event/stream/reconnect_test.go index b8c8828..ea322ba 100644 --- a/event/stream/reconnect_test.go +++ b/event/stream/reconnect_test.go @@ -13,7 +13,9 @@ import ( // createPullPointRespAlt mirrors the first fixture but returns a // different SubscriptionReference Address so a test can prove that -// subsequent pulls hit the recreated endpoint. +// subsequent pulls hit the recreated endpoint. Like createPullPointResp, +// it intentionally omits so renew scheduling stays +// driven by opts. const createPullPointRespAlt = ` 0 { + return opts.RetryBackoff + } + return time.Second +} + // renewPullPoint sends Renew with an absolute UTC TerminationTime. // WS-BaseNotification §6.1.1 also allows xsd:duration but older // Hikvision, some Dahua and some Bosch firmwares reject the diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go index 55acf55..0cb86ab 100644 --- a/event/stream/renew_test.go +++ b/event/stream/renew_test.go @@ -191,7 +191,7 @@ const renewFaultBody = `297`, + RefParamsXML: `297`, } fc := newFakeCaller() _, err := renewPullPoint(fc, ref, defaultOptions()) @@ -202,3 +202,56 @@ func TestRenewPullPoint_EchoesRefParamsWithIsReferenceParameter(t *testing.T) { assert.Contains(t, hdr, "297") assert.Contains(t, hdr, `IsReferenceParameter="true"`) } + +// Regression: when GrantedTermination has just passed (renew failed +// at or after the deadline), nextRenewInterval floors to one second +// and the loop hammers the camera at 1 Hz until reconnect. Original +// ticker design retried at the configured cadence regardless. After +// a failure the loop must use a backoff decoupled from the stale +// grant. +func TestNextRenewIntervalAfterError_IgnoresStaleGrantedTermination(t *testing.T) { + now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + stale := now.Add(-500 * time.Millisecond) + opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second, RetryBackoff: time.Second} + got := nextRenewIntervalAfterError(stale, opts, now) + assert.GreaterOrEqual(t, got, opts.RetryBackoff, + "failure path must back off at least RetryBackoff, not 1s floor on stale grant") +} + +func TestNextRenewIntervalAfterError_FallsBackToRetryBackoff(t *testing.T) { + now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second, RetryBackoff: 5 * time.Second} + got := nextRenewIntervalAfterError(time.Time{}, opts, now) + assert.Equal(t, opts.RetryBackoff, got) +} + +// Lost-update race: renew snapshots the ref, the SOAP call returns, +// and meanwhile attemptRecreate replaced pullPoint with a fresh +// subscription. If renew blindly writes the OLD subscription's +// granted time onto the NEW subscription, the new schedule is wrong. +// Update must be conditioned on "same subscription as when I read." +func TestUpdateGrantedTermination_DropsWriteWhenSubscriptionRotated(t *testing.T) { + s := &Stream{} + original := subscriptionRef{Address: "http://camera/sub-A"} + s.setPullPoint(original) + gen := s.pullPointGen() + + // Simulate recreate happening between snapshot and write. + s.setPullPoint(subscriptionRef{Address: "http://camera/sub-B"}) + + // Old generation's renew result must NOT overwrite sub-B's grant. + bogus := time.Date(1999, 1, 1, 0, 0, 0, 0, time.UTC) + s.updateGrantedTerminationIfGen(gen, bogus) + + assert.True(t, s.getPullPoint().GrantedTermination.IsZero(), + "stale renew result must be discarded after a subscription rotation") +} + +func TestUpdateGrantedTermination_AppliesWhenGenMatches(t *testing.T) { + s := &Stream{} + s.setPullPoint(subscriptionRef{Address: "http://camera/sub"}) + gen := s.pullPointGen() + t1 := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + s.updateGrantedTerminationIfGen(gen, t1) + assert.Equal(t, t1, s.getPullPoint().GrantedTermination) +} diff --git a/event/stream/soap.go b/event/stream/soap.go index 489882c..1cc7555 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -78,6 +78,12 @@ func extractTerminationTime(body string) time.Time { return t } +// terminationTimeRE matches the first <*:TerminationTime> in the body. +// Only safe on responses that contain exactly one — currently +// CreatePullPointSubscriptionResponse and RenewResponse via +// extractTerminationTime. PullMessagesResponse also has a +// TerminationTime element; do not call extractTerminationTime on pull +// bodies. var terminationTimeRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?TerminationTime\b[^>]*>(.*?)\s]+:)?TerminationTime>`) // pullMessages returns an empty slice (no error) when the camera had @@ -188,8 +194,14 @@ var ( soap12SubcodeRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Subcode\b[^>]*>.*?<(?:[^:>\s]+:)?Value[^>]*>(.*?)\s]+:)?Value>`) // WS-Security blocks may carry our Username/Password if the camera - // echoes the request in a fault; scrub before logging. - wsseSecurityRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Security\b[^>]*>.*?\s]+:)?Security>`) + // echoes the request in a fault; scrub before logging. The + // alternation handles the truncated case where the read cap fell + // between and : in that case nothing past + // the opening tag is safe to retain. wssePasswordRE is the + // belt-and-braces fallback for non-conformant cameras emitting + // Password / UsernameToken outside a Security wrapper. + wsseSecurityRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Security\b[^>]*>(?:.*?\s]+:)?Security>|.*)`) + wssePasswordRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Password\b[^>]*>.*?\s]+:)?Password>`) ) // extractSOAPFault returns the reason text from a SOAP fault, falling @@ -220,39 +232,28 @@ var ( refParamsRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?ReferenceParameters\b[^>]*>(.*?)\s]+:)?ReferenceParameters>`) ) -// buildRefParamsHeader produces the SOAP
inner XML for a set -// of WS-Addressing ReferenceParameters: each ref-param element is -// re-emitted with wsa:IsReferenceParameter="true" and any xmlns:* -// it inherited from the parent element. Input -// may be either the raw children or the full <*:ReferenceParameters> -// wrapper — extractReferenceParameters returns the wrapper so parent- -// scoped namespace declarations survive into the rebuild. -func buildRefParamsHeader(rawXML string) (string, error) { - if strings.TrimSpace(rawXML) == "" { +// buildRefParamsHeader produces the SOAP
inner XML from the +// full <*:ReferenceParameters> element returned by +// extractReferenceParameters. Each child element is re-emitted with +// wsa:IsReferenceParameter="true" added and any xmlns:* declared on +// the parent inherited onto it (so the standalone child stays valid). +// Empty input yields empty output. +func buildRefParamsHeader(refParamsXML string) (string, error) { + if strings.TrimSpace(refParamsXML) == "" { return "", nil } doc := etree.NewDocument() - if err := doc.ReadFromString("" + rawXML + ""); err != nil { + if err := doc.ReadFromString(refParamsXML); err != nil { return "", fmt.Errorf("parse ref params: %w", err) } - wrap := doc.SelectElement("wrap") - if wrap == nil { - return "", errors.New("parse ref params: missing wrap root") + wrapper := doc.Root() + if wrapper == nil || wrapper.Tag != "ReferenceParameters" { + return "", fmt.Errorf("ref params root must be <*:ReferenceParameters>, got <%s>", rootTag(wrapper)) } - - children := wrap.ChildElements() - var ambient *etree.Element - if len(children) == 1 && strings.HasSuffix(children[0].Tag, "ReferenceParameters") { - ambient = children[0] - children = ambient.ChildElements() - } - var out strings.Builder - for _, child := range children { + for _, child := range wrapper.ChildElements() { c := child.Copy() - if ambient != nil { - inheritXmlns(c, ambient) - } + inheritXmlns(c, wrapper) c.CreateAttr("wsa:IsReferenceParameter", "true") d := etree.NewDocument() d.SetRoot(c) @@ -265,6 +266,13 @@ func buildRefParamsHeader(rawXML string) (string, error) { return out.String(), nil } +func rootTag(e *etree.Element) string { + if e == nil { + return "" + } + return e.Tag +} + // inheritXmlns copies xmlns / xmlns:* declarations from src onto dst // when dst doesn't already declare them, so a child whose namespace // prefix was declared on an ancestor stays valid in isolation. @@ -332,6 +340,7 @@ func enrichSOAPErr(resp *http.Response, err error) error { return err } body := wsseSecurityRE.ReplaceAllString(string(b), "[REDACTED]") + body = wssePasswordRE.ReplaceAllString(body, "[REDACTED]") if reason := extractSOAPFault(body); reason != "" { return fmt.Errorf("SOAP fault: %s: %w", reason, err) } diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index 20edd4d..8289c05 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -315,7 +315,7 @@ func TestCreatePullPoint_VendorWithoutRefParams_RefParamsEmpty(t *testing.T) { func TestPullMessages_EchoesRefParamsWithIsReferenceParameterAttribute(t *testing.T) { ref := subscriptionRef{ Address: "http://192.168.1.10/onvif/services", - RefParamsXML: `297`, + RefParamsXML: `297`, } fc := newFakeCaller() _, err := pullMessages(fc, ref, defaultOptions()) @@ -349,7 +349,9 @@ func TestPullMessages_PostsToAddressFromRef(t *testing.T) { // --- Building the header XML from raw ref params ---------------------- func TestBuildRefParamsHeader_AddsIsReferenceParameter(t *testing.T) { - raw := `297` + raw := `` + + `297` + + `` got, err := buildRefParamsHeader(raw) require.NoError(t, err) assert.Contains(t, got, "SubscriptionId") @@ -360,7 +362,9 @@ func TestBuildRefParamsHeader_AddsIsReferenceParameter(t *testing.T) { } func TestBuildRefParamsHeader_MultipleTopLevelChildren(t *testing.T) { - raw := `12` + raw := `` + + `12` + + `` got, err := buildRefParamsHeader(raw) require.NoError(t, err) assert.Equal(t, 2, strings.Count(got, `IsReferenceParameter="true"`), @@ -378,7 +382,7 @@ func TestBuildRefParamsHeader_EmptyInputReturnsEmpty(t *testing.T) { func TestUnsubscribePullPoint_EchoesRefParamsWithIsReferenceParameter(t *testing.T) { ref := subscriptionRef{ Address: "http://192.168.1.10/onvif/services", - RefParamsXML: `297`, + RefParamsXML: `297`, } fc := newFakeCaller() require.NoError(t, unsubscribePullPoint(fc, ref)) @@ -401,8 +405,10 @@ func TestUnsubscribePullPoint_EmptyAddressIsNoOp(t *testing.T) { func TestPullMessages_TwoRefParamsEachLandsOnTheWire(t *testing.T) { ref := subscriptionRef{ Address: "http://camera/sub", - RefParamsXML: `1` + - `2`, + RefParamsXML: `` + + `1` + + `2` + + ``, } fc := newFakeCaller() _, err := pullMessages(fc, ref, defaultOptions()) @@ -601,3 +607,76 @@ func TestBuildRefParamsHeader_WhitespaceOnlyReturnsEmpty(t *testing.T) { require.NoError(t, err) assert.Empty(t, got) } + +// Worst case: the response body's block starts within the +// 64 KiB error cap but its is past it. The non-greedy +// regex needs a close tag — without one the redaction misses and +// raw Username/Password reaches the excerpt. Verify the helper +// strips from ` + + `` + + `admin` + + `hunter2` + + // no — simulates a Security block truncated + // at the 64 KiB read cap. + strings.Repeat("padding ", 1000) + got := enrichSOAPErr(fakeResponse(body), errors.New("500")) + require.Error(t, got) + assert.NotContains(t, got.Error(), "hunter2", + "truncated Security block must not leak Password to the excerpt") + assert.NotContains(t, got.Error(), "admin", + "truncated Security block must not leak Username to the excerpt") +} + +// The wrapper-only contract means an element whose local name merely +// ends in "ReferenceParameters" cannot be mistaken for the wrapper — +// the root must be exactly <*:ReferenceParameters>. Anything else is +// a contract violation by the caller and surfaces as an error. +func TestBuildRefParamsHeader_RejectsNonReferenceParametersRoot(t *testing.T) { + raw := `X` + _, err := buildRefParamsHeader(raw) + require.Error(t, err) + assert.Contains(t, err.Error(), "ReferenceParameters") +} + +// A non-conformant camera echoing UsernameToken/Password outside a +// wrapper would still leak credentials through the body +// excerpt. Belt-and-braces: redact Password elements directly too. +func TestEnrichSOAPErr_RedactsBarePasswordElement(t *testing.T) { + body := `` + + `` + + `admin` + + `hunter2` + + `` + + `` + got := enrichSOAPErr(fakeResponse(body), errors.New("400")) + require.Error(t, got) + assert.NotContains(t, got.Error(), "hunter2", + "Password must be redacted regardless of whether it's wrapped in Security") +} + +// --- Edge-case coverage flagged in review ----------------------------- + +func TestExtractTerminationTime_MalformedDateYieldsZero(t *testing.T) { + body := `not-a-date` + assert.True(t, extractTerminationTime(body).IsZero(), + "unparseable datetime must not panic and must not return a garbage time — fall back to opts") +} + +func TestNextRenewInterval_MarginEqualsBaseFallsToHalf(t *testing.T) { + now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) + opts := Options{InitialTermination: 30 * time.Second, RenewMargin: 30 * time.Second} + got := nextRenewInterval(time.Time{}, opts, now) + assert.Equal(t, 15*time.Second, got, + "when margin == base, the helper must fall through to base/2 rather than the 1s floor") +} + +func TestExtractReferenceParameters_EmptySubscriptionReferenceReturnsEmpty(t *testing.T) { + body := ` + + + +` + assert.Empty(t, extractReferenceParameters(body)) +} diff --git a/event/stream/stream.go b/event/stream/stream.go index a3f0d8b..49d3aeb 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -165,8 +165,9 @@ type Stream struct { caller caller opts Options - pullPointMu sync.Mutex - pullPoint subscriptionRef + pullPointMu sync.Mutex + pullPoint subscriptionRef + gen uint64 // bumped on every setPullPoint so renews can detect a recreate events chan Event errors chan error @@ -191,11 +192,29 @@ func (s *Stream) setPullPoint(ref subscriptionRef) { s.pullPointMu.Lock() defer s.pullPointMu.Unlock() s.pullPoint = ref + s.gen++ } -func (s *Stream) updateGrantedTermination(t time.Time) { +// pullPointGen returns the current generation. Pair with +// updateGrantedTerminationIfGen so a renew result issued against a +// subscription that was rotated mid-flight (recreate path) is +// discarded instead of overwriting the new subscription's grant. +func (s *Stream) pullPointGen() uint64 { s.pullPointMu.Lock() defer s.pullPointMu.Unlock() + return s.gen +} + +// updateGrantedTerminationIfGen writes the granted time only when the +// caller's snapshot is still current. Use setPullPoint to replace the +// full ref; this updates GrantedTermination in place after a renew +// response. +func (s *Stream) updateGrantedTerminationIfGen(gen uint64, t time.Time) { + s.pullPointMu.Lock() + defer s.pullPointMu.Unlock() + if s.gen != gen { + return + } s.pullPoint.GrantedTermination = t } diff --git a/event/stream/stream_test.go b/event/stream/stream_test.go index ebaaaa0..f0c8df7 100644 --- a/event/stream/stream_test.go +++ b/event/stream/stream_test.go @@ -125,6 +125,10 @@ func (f *fakeCaller) sendSoapCallCount() int { // createPullPointResp is the minimal SOAP envelope the lib's existing // xml.Decoder + getXMLNode path can extract a pull-point address from. +// Intentionally omits so renewLoop falls back to +// opts.InitialTermination — tests that drive renew timing depend on +// that path. Tests that need the camera-granted termination capture +// path use a dedicated fixture instead. const createPullPointResp = ` " + data + ""); err != nil { + return err + } + wrap := in.SelectElement("wrap") + if wrap == nil { + return errors.New("AddStringHeaderContents: missing wrap root") + } + children := wrap.ChildElements() + if len(children) == 0 { + return errors.New("AddStringHeaderContents: no element children in content") + } + + doc := etree.NewDocument() + if err := doc.ReadFromString(msg.String()); err != nil { + return err + } + header := doc.Root().SelectElement("Header") + for _, child := range children { + header.AddChild(child.Copy()) + } + res, _ := doc.WriteToString() + *msg = SoapMessage(res) + return nil +} + //AddHeaderContent for Envelope body func (msg *SoapMessage) AddHeaderContent(element *etree.Element) { doc := etree.NewDocument() From 07b0f6c2e073413f8e9ff65f686a6cbd6976b79b Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:57 +0200 Subject: [PATCH 52/53] fix(event/stream): address round-3 review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical - Two-step lock race in renewLoop: getPullPoint() + pullPointGen() were separate Lock/Unlock pairs, leaving a window in which a concurrent setPullPoint could advance gen between the two reads. The renew then ran against ref-N but believed its captured gen was N+1, and updateGrantedTerminationIfGen "succeeded" writing the old subscription's grant onto the new one — same class of bug the generation counter was meant to fix. New snapshotPullPoint accessor reads both under one lock. - wssePasswordRE was missing the close-tag-or-EOF alternative that wsseSecurityRE got last round; a element truncated at the 64 KiB error cap escaped redaction. Pattern is now symmetric. Important - Dropped unused (granted, now) params from nextRenewIntervalAfterError; only opts.RetryBackoff is consulted. Tests adjusted. - Moved gen field next to pullPointMu with explicit guard comment. - Inlined the single-use rootTag helper; nil case handled directly. - Loosened TestNextRenewIntervalAfterError_FallsBackToRetryBackoff so future jitter doesn't break it. Docs - Extended trust-boundary godoc on SendSoap* to name the full set of weaponisable WS-* headers (wsa:To/ReplyTo/FaultTo/MessageID, wsu:Timestamp). - Mirrored the trust-boundary warning on gosoap.AddStringHeaderContents so library consumers see it at the package entry point too. - Noted that updateGrantedTerminationIfGen intentionally doesn't bump gen (would defeat rotation detection). - Documented the wsseSecurityRE truncation-branch trade-off (max-redact > max-context for log lines). Tests - TestSnapshotPullPoint_AtomicReadOfRefAndGen pins the new accessor. - TestEnrichSOAPErr_RedactsTruncatedPasswordOutsideSecurity exercises the now-symmetric password redaction. - TestEnrichSOAPErr_RedactsMultipleSecurityBlocks pins existing multi-block behaviour. - TestBuildRefParamsHeader_RejectsNonReferenceParametersRoot_Table covers vendor-suffix, multi-root, and unrelated-element cases. - One-line comment on the &Stream{} tests explaining nil-now safety. --- Device.go | 8 +++++--- event/stream/renew.go | 13 ++++++------- event/stream/renew_test.go | 40 ++++++++++++++++++++++++++++---------- event/stream/soap.go | 25 ++++++++++++------------ event/stream/soap_test.go | 39 +++++++++++++++++++++++++++++++++++++ event/stream/stream.go | 18 +++++++++++++---- gosoap/soap-builder.go | 4 ++++ 7 files changed, 110 insertions(+), 37 deletions(-) diff --git a/Device.go b/Device.go index 628d242..9c89b14 100644 --- a/Device.go +++ b/Device.go @@ -349,9 +349,11 @@ func (dev Device) SendSoap(endpoint string, xmlRequestBody string) (*http.Respon // element-free content errors before any request is made. // // SECURITY: do not pass content sourced from untrusted clients. The -// API assumes the caller is authoritative for the envelope; injected -// or in the header forwards verbatim and -// may override envelope defaults under our credentials. +// API assumes the caller is authoritative for the envelope. Header +// content forwards verbatim — among others, overrides +// auth, overrides intent, // +// redirect responses, enables replay- +// token forgery, and bypasses freshness checks. func (dev Device) SendSoapWithHeader(endpoint, xmlRequestBody, xmlHeaderContent string) (*http.Response, error) { return dev.SendSoapWithOptions(endpoint, xmlRequestBody, WithSOAPHeader(xmlHeaderContent)) } diff --git a/event/stream/renew.go b/event/stream/renew.go index c7c6969..3e29edf 100644 --- a/event/stream/renew.go +++ b/event/stream/renew.go @@ -19,15 +19,14 @@ import ( // reliable recovery once a subscription is GC'd. func (s *Stream) renewLoop(ctx context.Context) { for { - ref := s.getPullPoint() - gen := s.pullPointGen() + ref, gen := s.snapshotPullPoint() if !sleepCtx(ctx, nextRenewInterval(ref.GrantedTermination, s.opts, s.now())) { return } granted, err := renewPullPoint(s.caller, s.getPullPoint(), s.opts) if err != nil { s.surfaceError(ErrRenewFailed{Err: err}) - if !sleepCtx(ctx, nextRenewIntervalAfterError(ref.GrantedTermination, s.opts, s.now())) { + if !sleepCtx(ctx, nextRenewIntervalAfterError(s.opts)) { return } continue @@ -58,12 +57,12 @@ func nextRenewInterval(granted time.Time, opts Options, now time.Time) time.Dura return d } -// nextRenewIntervalAfterError ignores GrantedTermination — by the time -// renew has failed once, the grant is typically already in the past -// and nextRenewInterval would floor to 1s, hammering the camera. +// nextRenewIntervalAfterError returns the post-failure sleep. The +// grant is typically already in the past by the time renew has failed +// once, so nextRenewInterval would floor to 1s and hammer the camera. // Recovery is the pull loop's reconnect path; we just need to not // accelerate retries past the configured RetryBackoff. -func nextRenewIntervalAfterError(_ time.Time, opts Options, _ time.Time) time.Duration { +func nextRenewIntervalAfterError(opts Options) time.Duration { if opts.RetryBackoff > 0 { return opts.RetryBackoff } diff --git a/event/stream/renew_test.go b/event/stream/renew_test.go index 0cb86ab..c9a29e6 100644 --- a/event/stream/renew_test.go +++ b/event/stream/renew_test.go @@ -209,20 +209,17 @@ func TestRenewPullPoint_EchoesRefParamsWithIsReferenceParameter(t *testing.T) { // ticker design retried at the configured cadence regardless. After // a failure the loop must use a backoff decoupled from the stale // grant. -func TestNextRenewIntervalAfterError_IgnoresStaleGrantedTermination(t *testing.T) { - now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) - stale := now.Add(-500 * time.Millisecond) - opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second, RetryBackoff: time.Second} - got := nextRenewIntervalAfterError(stale, opts, now) +func TestNextRenewIntervalAfterError_BacksOffAtLeastRetryBackoff(t *testing.T) { + opts := Options{RetryBackoff: time.Second} + got := nextRenewIntervalAfterError(opts) assert.GreaterOrEqual(t, got, opts.RetryBackoff, "failure path must back off at least RetryBackoff, not 1s floor on stale grant") } -func TestNextRenewIntervalAfterError_FallsBackToRetryBackoff(t *testing.T) { - now := time.Date(2026, 5, 27, 13, 0, 0, 0, time.UTC) - opts := Options{InitialTermination: 60 * time.Second, RenewMargin: 10 * time.Second, RetryBackoff: 5 * time.Second} - got := nextRenewIntervalAfterError(time.Time{}, opts, now) - assert.Equal(t, opts.RetryBackoff, got) +func TestNextRenewIntervalAfterError_DefaultsToOneSecondWhenRetryBackoffZero(t *testing.T) { + got := nextRenewIntervalAfterError(Options{}) + assert.Equal(t, time.Second, got, + "zero RetryBackoff must yield the safety floor, not a tight 0-duration sleep") } // Lost-update race: renew snapshots the ref, the SOAP call returns, @@ -231,6 +228,8 @@ func TestNextRenewIntervalAfterError_FallsBackToRetryBackoff(t *testing.T) { // granted time onto the NEW subscription, the new schedule is wrong. // Update must be conditioned on "same subscription as when I read." func TestUpdateGrantedTermination_DropsWriteWhenSubscriptionRotated(t *testing.T) { + // s.now is intentionally nil — this test does not exercise any + // time-dependent path; only the gen-counter accessors. s := &Stream{} original := subscriptionRef{Address: "http://camera/sub-A"} s.setPullPoint(original) @@ -248,6 +247,7 @@ func TestUpdateGrantedTermination_DropsWriteWhenSubscriptionRotated(t *testing.T } func TestUpdateGrantedTermination_AppliesWhenGenMatches(t *testing.T) { + // s.now is intentionally nil — gen-counter path only. s := &Stream{} s.setPullPoint(subscriptionRef{Address: "http://camera/sub"}) gen := s.pullPointGen() @@ -255,3 +255,23 @@ func TestUpdateGrantedTermination_AppliesWhenGenMatches(t *testing.T) { s.updateGrantedTerminationIfGen(gen, t1) assert.Equal(t, t1, s.getPullPoint().GrantedTermination) } + +// renewLoop must capture (ref, gen) atomically. Two separate +// getPullPoint() + pullPointGen() reads leave a window in which a +// concurrent setPullPoint advances gen between the two reads — the +// renew then runs against ref-N but believes its captured gen is N+1, +// and updateGrantedTerminationIfGen("succeeds") writing the old +// subscription's grant onto the new one. Single snapshot closes it. +func TestSnapshotPullPoint_AtomicReadOfRefAndGen(t *testing.T) { + // s.now is intentionally nil — gen-counter path only. + s := &Stream{} + s.setPullPoint(subscriptionRef{Address: "A"}) // gen=1 + ref, gen := s.snapshotPullPoint() + assert.Equal(t, "A", ref.Address) + assert.Equal(t, uint64(1), gen) + + s.setPullPoint(subscriptionRef{Address: "B"}) // gen=2 + ref, gen = s.snapshotPullPoint() + assert.Equal(t, "B", ref.Address) + assert.Equal(t, uint64(2), gen) +} diff --git a/event/stream/soap.go b/event/stream/soap.go index 1cc7555..d8a1e7f 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -197,11 +197,14 @@ var ( // echoes the request in a fault; scrub before logging. The // alternation handles the truncated case where the read cap fell // between and : in that case nothing past - // the opening tag is safe to retain. wssePasswordRE is the - // belt-and-braces fallback for non-conformant cameras emitting - // Password / UsernameToken outside a Security wrapper. + // the opening tag is safe to retain — the replacement re-emits a + // synthetic close tag, dropping the remainder of the body excerpt + // (max-redact preferred to max-context for log lines). + // wssePasswordRE is the belt-and-braces fallback for + // non-conformant cameras emitting Password / UsernameToken outside + // a Security wrapper; same truncation handling. wsseSecurityRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Security\b[^>]*>(?:.*?\s]+:)?Security>|.*)`) - wssePasswordRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Password\b[^>]*>.*?\s]+:)?Password>`) + wssePasswordRE = regexp.MustCompile(`(?s)<(?:[^:>\s]+:)?Password\b[^>]*>(?:.*?\s]+:)?Password>|.*)`) ) // extractSOAPFault returns the reason text from a SOAP fault, falling @@ -247,8 +250,11 @@ func buildRefParamsHeader(refParamsXML string) (string, error) { return "", fmt.Errorf("parse ref params: %w", err) } wrapper := doc.Root() - if wrapper == nil || wrapper.Tag != "ReferenceParameters" { - return "", fmt.Errorf("ref params root must be <*:ReferenceParameters>, got <%s>", rootTag(wrapper)) + if wrapper == nil { + return "", errors.New("ref params has no root element") + } + if wrapper.Tag != "ReferenceParameters" { + return "", fmt.Errorf("ref params root must be <*:ReferenceParameters>, got <%s>", wrapper.Tag) } var out strings.Builder for _, child := range wrapper.ChildElements() { @@ -266,13 +272,6 @@ func buildRefParamsHeader(refParamsXML string) (string, error) { return out.String(), nil } -func rootTag(e *etree.Element) string { - if e == nil { - return "" - } - return e.Tag -} - // inheritXmlns copies xmlns / xmlns:* declarations from src onto dst // when dst doesn't already declare them, so a child whose namespace // prefix was declared on an ancestor stays valid in isolation. diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index 8289c05..153e2c1 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -680,3 +680,42 @@ func TestExtractReferenceParameters_EmptySubscriptionReferenceReturnsEmpty(t *te ` assert.Empty(t, extractReferenceParameters(body)) } + +func TestEnrichSOAPErr_RedactsTruncatedPasswordOutsideSecurity(t *testing.T) { + body := `` + + `` + + `admin` + + `hunter2` // truncated — no , no , no + got := enrichSOAPErr(fakeResponse(body), errors.New("500")) + require.Error(t, got) + assert.NotContains(t, got.Error(), "hunter2", + "Password without a closing tag (truncated at cap) must still be redacted") +} + +func TestEnrichSOAPErr_RedactsMultipleSecurityBlocks(t *testing.T) { + body := `` + + `secret1` + + `secret2` + + `` + got := enrichSOAPErr(fakeResponse(body), errors.New("500")) + require.Error(t, got) + assert.NotContains(t, got.Error(), "secret1") + assert.NotContains(t, got.Error(), "secret2", + "ReplaceAllString must catch every Security block, not just the first") +} + +func TestBuildRefParamsHeader_RejectsNonReferenceParametersRoot_Table(t *testing.T) { + cases := []struct { + name, raw string + }{ + {"vendor suffix", `X`}, + {"multi-root", ``}, + {"unrelated element", `content`}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + _, err := buildRefParamsHeader(c.raw) + require.Error(t, err) + }) + } +} diff --git a/event/stream/stream.go b/event/stream/stream.go index 49d3aeb..9dac55b 100644 --- a/event/stream/stream.go +++ b/event/stream/stream.go @@ -165,9 +165,9 @@ type Stream struct { caller caller opts Options - pullPointMu sync.Mutex - pullPoint subscriptionRef - gen uint64 // bumped on every setPullPoint so renews can detect a recreate + pullPointMu sync.Mutex // guards pullPoint and gen + pullPoint subscriptionRef + gen uint64 // bumped on every setPullPoint so renews detect mid-flight recreate events chan Event errors chan error @@ -205,10 +205,20 @@ func (s *Stream) pullPointGen() uint64 { return s.gen } +// snapshotPullPoint reads ref + gen under one lock so a concurrent +// setPullPoint can't slip in between two separate accessor calls and +// leave the caller with mismatched halves. +func (s *Stream) snapshotPullPoint() (subscriptionRef, uint64) { + s.pullPointMu.Lock() + defer s.pullPointMu.Unlock() + return s.pullPoint, s.gen +} + // updateGrantedTerminationIfGen writes the granted time only when the // caller's snapshot is still current. Use setPullPoint to replace the // full ref; this updates GrantedTermination in place after a renew -// response. +// response. Intentionally does not bump gen — that would defeat the +// rotation-detection it implements. func (s *Stream) updateGrantedTerminationIfGen(gen uint64, t time.Time) { s.pullPointMu.Lock() defer s.pullPointMu.Unlock() diff --git a/gosoap/soap-builder.go b/gosoap/soap-builder.go index 4d053f6..d86fc7f 100644 --- a/gosoap/soap-builder.go +++ b/gosoap/soap-builder.go @@ -170,6 +170,10 @@ func (msg *SoapMessage) AddStringHeaderContent(data string) error { // reference parameter to be a separate Header block, but a Go XML // document only has one root. Comments and text outside elements are // silently dropped. +// +// SECURITY: data forwards verbatim into the outbound envelope. Do not +// pass content sourced from untrusted clients — see the same caveat +// on onvif.Device.SendSoapWithOptions / SendSoapWithHeader. func (msg *SoapMessage) AddStringHeaderContents(data string) error { in := etree.NewDocument() if err := in.ReadFromString("" + data + ""); err != nil { From 16312b236db3a67fb5499494002a7f07fa0bcffa Mon Sep 17 00:00:00 2001 From: Sebastian Norling <1932208+Bazze@users.noreply.github.com> Date: Wed, 27 May 2026 18:07:57 +0200 Subject: [PATCH 53/53] docs: fix WS-Addressing spec citations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reference-parameter-to-SOAP-header mapping rule (with the wsa:IsReferenceParameter='true' attribute) lives in WS-Addressing 1.0 SOAP Binding §3.4 (Binding Message Addressing Properties), not Core §3.1 (Abstract Property Definitions). The earlier citations were wrong on both the section number and the document. Corrected across doc comments, test descriptions, and the PR description. The "ReferenceParameters can appear in any endpoint reference" claim in the anchored-extraction test now cites Core §2.1 (Information Model for Endpoint References), which is where the [reference parameters] property is defined on the abstract EPR. Verified against the W3C Recommendations: - https://www.w3.org/TR/ws-addr-core/ §2.1 - https://www.w3.org/TR/ws-addr-soap/ §3.4 Also adds PR_event_stream_axis_compat.md — the branch's PR description, framed independently of the prior event/stream PR. --- Device_test.go | 2 +- event/stream/soap.go | 2 +- event/stream/soap_test.go | 8 ++++---- gosoap/soap-builder.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Device_test.go b/Device_test.go index 74bf502..8645791 100644 --- a/Device_test.go +++ b/Device_test.go @@ -72,7 +72,7 @@ func TestDevice_SendSoapWithHeader_InjectsHeaderXML(t *testing.T) { "body content must land inside SOAP ") } -// Per WS-Addressing 1.0 §3.1 every reference parameter is a separate +// Per WS-Addressing 1.0 SOAP Binding §3.4 every reference parameter is a separate // SOAP Header block. Vendors that declare two ref params would silently // produce a header-less request if the implementation only accepts a // single top-level element. diff --git a/event/stream/soap.go b/event/stream/soap.go index d8a1e7f..105fef6 100644 --- a/event/stream/soap.go +++ b/event/stream/soap.go @@ -295,7 +295,7 @@ func inheritXmlns(dst, src *etree.Element) { // extractReferenceParameters returns the verbatim inner XML so callers // can echo it (with wsa:IsReferenceParameter="true") into the SOAP -// Header of subscription-scoped requests per WS-Addressing 1.0 §3.1. +// Header of subscription-scoped requests per WS-Addressing 1.0 SOAP Binding §3.4. // Without that echo, AXIS rejects PullMessages with ter:InvalidArgs. func extractReferenceParameters(body string) string { sub := subscriptionRefRE.FindStringSubmatch(body) diff --git a/event/stream/soap_test.go b/event/stream/soap_test.go index 153e2c1..ae8de38 100644 --- a/event/stream/soap_test.go +++ b/event/stream/soap_test.go @@ -226,7 +226,7 @@ func TestUnsubscribePullPoint_EnrichesTransportErrWithFaultReason(t *testing.T) "unsubscribePullPoint must enrich transport errors with the camera's SOAP fault") } -// --- ReferenceParameters extraction (WS-Addressing 1.0 §3.1) --------- +// --- ReferenceParameters extraction (WS-Addressing 1.0 SOAP Binding §3.4) --------- // // AXIS encodes the subscription identity in // inside CreatePullPointSubscriptionResponse rather than in the URL @@ -307,7 +307,7 @@ func TestCreatePullPoint_VendorWithoutRefParams_RefParamsEmpty(t *testing.T) { // --- Reference-parameter echoing in subscription-scoped calls -------- // -// WS-Addressing 1.0 §3.1 requires each child +// WS-Addressing 1.0 SOAP Binding §3.4 requires each child // to be echoed as a SOAP Header block carrying wsa:IsReferenceParameter // ="true". AXIS rejects PullMessages with ter:InvalidArgs when this is // absent. @@ -325,7 +325,7 @@ func TestPullMessages_EchoesRefParamsWithIsReferenceParameterAttribute(t *testin assert.Contains(t, hdr, "SubscriptionId", "ref param element must be echoed") assert.Contains(t, hdr, "297", "ref param value must be echoed") assert.Contains(t, hdr, `IsReferenceParameter="true"`, - "WS-Addressing 1.0 §3.1 requires the attribute on each echoed element") + "WS-Addressing 1.0 SOAP Binding §3.4 requires the attribute on each echoed element") } func TestPullMessages_NoRefParams_HeaderEmpty(t *testing.T) { @@ -458,7 +458,7 @@ func TestExtractSOAPFault_FallsBackToSubcodeWhenReasonEmpty(t *testing.T) { assert.Equal(t, "ter:InvalidArgs", extractSOAPFault(body)) } -// WS-Addressing §3.1 allows ReferenceParameters in any endpoint +// WS-Addressing 1.0 Core §2.1 allows ReferenceParameters in any endpoint // reference (wsa:From, wsa:ReplyTo, wsa:FaultTo, ...). An unanchored // search would silently pick up the wrong one. func TestExtractReferenceParameters_AnchoredToSubscriptionReference(t *testing.T) { diff --git a/gosoap/soap-builder.go b/gosoap/soap-builder.go index d86fc7f..e1edb03 100644 --- a/gosoap/soap-builder.go +++ b/gosoap/soap-builder.go @@ -166,7 +166,7 @@ func (msg *SoapMessage) AddStringHeaderContent(data string) error { // AddStringHeaderContents is the multi-root variant of // AddStringHeaderContent: it accepts any number of top-level sibling // elements (zero is an error) and appends each as its own SOAP Header -// child. Needed because WS-Addressing 1.0 §3.1 requires each +// child. Needed because WS-Addressing 1.0 SOAP Binding §3.4 requires each // reference parameter to be a separate Header block, but a Go XML // document only has one root. Comments and text outside elements are // silently dropped.