Skip to content

Commit

Permalink
Implements SystemDateAndTime schema as well as Get and Set operations…
Browse files Browse the repository at this point in the history
… with tests (#71)
  • Loading branch information
paoloo authored Jul 11, 2024
1 parent 050db0f commit b706680
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 52 deletions.
13 changes: 11 additions & 2 deletions lib/device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ defmodule Onvif.Device do
:auth_type,
:time_diff_from_system_secs,
:port,
:device_service_path
:device_service_path,
:system_date_time
]

@type t :: %__MODULE__{}
Expand All @@ -41,6 +42,7 @@ defmodule Onvif.Device do
field(:ntp, :string)
field(:media_ver10_service_path, :string)
field(:media_ver20_service_path, :string)
embeds_one(:system_date_time, Onvif.Devices.SystemDateAndTime)
embeds_many(:services, Onvif.Device.Service)

field(:auth_type, Ecto.Enum,
Expand Down Expand Up @@ -84,6 +86,7 @@ defmodule Onvif.Device do
def changeset(%__MODULE__{} = device, attrs \\ %{}) do
device
|> cast(attrs, @required ++ @optional)
|> cast_embed(:system_date_time, with: &Onvif.Devices.SystemDateAndTime.changeset/2)
|> cast_embed(:services, with: &Onvif.Device.Service.changeset/2)
|> validate_required(@required)
end
Expand Down Expand Up @@ -235,7 +238,13 @@ defmodule Onvif.Device do

defp get_date_time(device) do
with {:ok, res} <- Onvif.Devices.GetSystemDateAndTime.request(device) do
updated_device = %{device | time_diff_from_system_secs: res.current_diff, ntp: res.ntp}
updated_device = %{
device
| time_diff_from_system_secs: res.current_diff,
ntp: res.date_time_type,
system_date_time: res
}

{:ok, updated_device}
end
end
Expand Down
15 changes: 12 additions & 3 deletions lib/devices/devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,34 @@ defmodule Onvif.Devices do
alias Onvif.Device

@namespaces [
"xmlns:tds": "http://www.onvif.org/ver10/device/wsdl"
"xmlns:tds": "http://www.onvif.org/ver10/device/wsdl",
"xmlns:tt": "http://www.onvif.org/ver10/schema"
]

@spec request(Device.t(), module()) :: {:ok, any} | {:error, map()}
def request(%Device{} = device, operation) do
content = generate_content(operation)
soap_action = operation.soap_action()
do_request(device, operation, content)
end

def request(%Device{} = device, args, operation) do
content = generate_content(operation, args)
do_request(device, operation, content)
end

defp do_request(device, operation, content) do
device
|> Onvif.API.client()
|> Tesla.request(
method: :post,
headers: [{"Content-Type", "application/soap+xml"}, {"SOAPAction", soap_action}],
headers: [{"Content-Type", "application/soap+xml"}, {"SOAPAction", operation.soap_action()}],
body: %Onvif.Request{content: content, namespaces: @namespaces}
)
|> parse_response(operation)
end

defp generate_content(operation), do: operation.request_body()
defp generate_content(operation, args), do: operation.request_body(args)

defp parse_response({:ok, %{status: 200, body: body}}, operation) do
operation.response(body)
Expand Down
56 changes: 10 additions & 46 deletions lib/devices/get_system_date_and_time.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,51 +18,15 @@ defmodule Onvif.Devices.GetSystemDateAndTime do
end

def response(xml_response_body) do
doc = parse(xml_response_body, namespace_conformant: true, quiet: true)

parsed_result =
xpath(
doc,
~x"//s:Envelope/s:Body/tds:GetSystemDateAndTimeResponse/tds:SystemDateAndTime"
|> add_namespace("s", "http://www.w3.org/2003/05/soap-envelope")
|> add_namespace("tds", "http://www.onvif.org/ver10/device/wsdl"),
hour:
~x"./tt:UTCDateTime/tt:Time/tt:Hour/text()"i
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
minute:
~x"./tt:UTCDateTime/tt:Time/tt:Minute/text()"i
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
second:
~x"./tt:UTCDateTime/tt:Time/tt:Second/text()"i
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
year:
~x"./tt:UTCDateTime/tt:Date/tt:Year/text()"i
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
month:
~x"./tt:UTCDateTime/tt:Date/tt:Month/text()"i
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
day:
~x"./tt:UTCDateTime/tt:Date/tt:Day/text()"i
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
timezone:
~x"./tt:Timezone/tt:TZ/text()"s
|> add_namespace("tt", "http://www.onvif.org/ver10/schema"),
ntp:
~x"./tt:DateTimeType/text()"s
|> add_namespace("tt", "http://www.onvif.org/ver10/schema")
)

current = DateTime.utc_now()
{:ok, date} = Date.new(parsed_result.year, parsed_result.month, parsed_result.day)
{:ok, time} = Time.new(parsed_result.hour, parsed_result.minute, parsed_result.second)
{:ok, datetime} = DateTime.new(date, time)
diff_between_device = DateTime.diff(datetime, current)

{:ok,
%Onvif.Devices.SystemDateAndTime{
datetime: datetime,
ntp: parsed_result.ntp,
current_diff: diff_between_device
}}
xml_response_body
|> parse(namespace_conformant: true, quiet: true)
|> xpath(
~x"//s:Envelope/s:Body/tds:GetSystemDateAndTimeResponse/tds:SystemDateAndTime"e
|> add_namespace("s", "http://www.w3.org/2003/05/soap-envelope")
|> add_namespace("tds", "http://www.onvif.org/ver10/device/wsdl")
|> add_namespace("tt", "http://www.onvif.org/ver10/schema")
)
|> Onvif.Devices.SystemDateAndTime.parse()
|> Onvif.Devices.SystemDateAndTime.to_struct()
end
end
83 changes: 83 additions & 0 deletions lib/devices/set_system_date_and_time.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
defmodule Onvif.Devices.SetSystemDateAndTime do
@moduledoc """
Module used to set the device system date and time as well as their configuration such as daylight saving and NTP or Manual update type on an ONVIF device.
If system time and date are set manually, the request shall include UTCDateTime.
A TimeZone token which is not formed according to the rules of IEEE 1003.1 section 8.3 is considered as invalid timezone.
The DayLightSavings flag should be set to true to activate any DST settings of the TimeZone string. Clear the DayLightSavings flag if the DST portion of the TimeZone settings should be ignored.
The configuration requires a %Onvif.Devices.SystemDateAndTime{} config and a bolean set_time? to manually change the time. It will be ignored if using NTP.
Ref: https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl#op.SetSystemDateAndTime
"""
import SweetXml
import XmlBuilder

alias Onvif.Device
alias Onvif.Devices.SystemDateAndTime

def soap_action, do: "http://www.onvif.org/ver10/device/wsdl/SetSystemDateAndTime"

@spec request(Device.t(), list) :: {:ok, any} | {:error, map()}
def request(device, args) do
Onvif.Devices.request(device, args, __MODULE__)
end

def request_body(config: %SystemDateAndTime{} = system_date_time) do
request_body(config: system_date_time, set_time?: false)
end

def request_body(config: %SystemDateAndTime{} = system_date_time, set_time?: set_time?) do
element(:"s:Body", [
element(:"tds:SetSystemDateAndTime", [
element(
:"tds:DateTimeType",
Keyword.fetch!(
Ecto.Enum.mappings(system_date_time.__struct__, :date_time_type),
system_date_time.date_time_type
)
),
element(:"tds:DaylightSavings", system_date_time.daylight_savings),
element(
:"tds:TimeZone",
[
element(:"tt:TZ", system_date_time.time_zone.tz)
]
),
List.flatten([utc_date_time_element(system_date_time, set_time?)])
])
])
end

def utc_date_time_element(_system_date_time, false), do: []

def utc_date_time_element(system_date_time, true) do
element(
:"tds:UTCDateTime",
[
element(:"tt:Time", [
element(:"tt:Hour", system_date_time.utc_date_time.time.hour),
element(:"tt:Minute", system_date_time.utc_date_time.time.minute),
element(:"tt:Second", system_date_time.utc_date_time.time.second)
]),
element(:"tt:Date", [
element(:"tt:Year", system_date_time.utc_date_time.date.year),
element(:"tt:Month", system_date_time.utc_date_time.date.month),
element(:"tt:Day", system_date_time.utc_date_time.date.day)
])
]
)
end

def response(xml_response_body) do
res =
xml_response_body
|> parse(namespace_conformant: true, quiet: true)
|> xpath(
~x"//s:Envelope/s:Body/tds:SetSystemDateAndTimeResponse/text()"s
|> add_namespace("s", "http://www.w3.org/2003/05/soap-envelope")
|> add_namespace("tds", "http://www.onvif.org/ver10/device/wsdl")
|> add_namespace("tt", "http://www.onvif.org/ver10/schema")
)

{:ok, res}
end
end
Loading

0 comments on commit b706680

Please sign in to comment.