hik_ISAPI 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/exe/hikvision +238 -0
- data/lib/hikvision/base.rb +82 -0
- data/lib/hikvision/isapi.rb +93 -0
- data/lib/hikvision/streaming/channel.rb +111 -0
- data/lib/hikvision/streaming.rb +27 -0
- data/lib/hikvision/system/acs_event.rb +15 -0
- data/lib/hikvision/system/device_info.rb +21 -0
- data/lib/hikvision/system/diagnosed_data.rb +7 -0
- data/lib/hikvision/system/face.rb +7 -0
- data/lib/hikvision/system/network/integration.rb +26 -0
- data/lib/hikvision/system/network.rb +11 -0
- data/lib/hikvision/system/person.rb +11 -0
- data/lib/hikvision/system/reboot.rb +7 -0
- data/lib/hikvision/system/status.rb +13 -0
- data/lib/hikvision/system/time/ntp.rb +28 -0
- data/lib/hikvision/system/time.rb +31 -0
- data/lib/hikvision/system.rb +11 -0
- data/lib/hikvision/version.rb +3 -0
- data/lib/hikvision.rb +17 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c238cc614d5f6b9b3c124405d90096f0b33f5cb00401a3f291bebd6151c293a8
|
4
|
+
data.tar.gz: 0c404fce16b367a021c1a1e6e732bf4899b3b6d06a4320192a3d73e2d0e72cb1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e97136d972327a68d62570162938d8eb66339d9357851124fff6a46662c635970809b1662cea396c923f62b29cf67b3262f0a2abd42cab9ecc5e25b6c3e2284
|
7
|
+
data.tar.gz: d3ed39978f2906cfbe3f96806fdb853e86feec7b3dc4b04f979c4fa2277446182fd9e6a79ec4a5f97512aaa093386cbb6a95c9456876e3e1c9f7ec6cbc8e714e
|
data/exe/hikvision
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'hikvision'
|
4
|
+
require 'gli'
|
5
|
+
|
6
|
+
class Numeric
|
7
|
+
def duration
|
8
|
+
secs = to_i
|
9
|
+
mins = secs / 60
|
10
|
+
hours = mins / 60
|
11
|
+
days = hours / 24
|
12
|
+
|
13
|
+
"#{days} days #{hours % 24} hours #{mins % 60} minutes #{secs % 60} seconds"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class App
|
18
|
+
extend GLI::App
|
19
|
+
|
20
|
+
def self.print_kv(key, value, opts)
|
21
|
+
puts "#{opts[:'with-keys'] ? key.to_s + ': ' : ''}#{value}" if opts[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.set_if_in_opts(obj, setter, val, opts)
|
25
|
+
return if val.nil?
|
26
|
+
|
27
|
+
if opts.include? val
|
28
|
+
obj.send(setter, val)
|
29
|
+
else
|
30
|
+
exit_now! "\"#{val}\" is invalid, choose from: #{opts.join(', ')}", 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
flag %i[h host], required: true
|
35
|
+
flag %i[u user], required: true
|
36
|
+
flag %i[p password], required: true
|
37
|
+
|
38
|
+
version Hikvision::VERSION
|
39
|
+
|
40
|
+
pre do |gopts, _opts, _args|
|
41
|
+
@dev = Hikvision::ISAPI.new(gopts[:h], gopts[:u], gopts[:p])
|
42
|
+
exit_now! 'could not authenticate' if @dev.put('/ISAPI/Security/sessionHeartbeat').response.code == '401'
|
43
|
+
1
|
44
|
+
end
|
45
|
+
|
46
|
+
command :uptime do |c|
|
47
|
+
c.action do
|
48
|
+
puts @dev.system.uptime.duration
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
command :date do |c|
|
53
|
+
c.flag :strftime, default_value: '%c'
|
54
|
+
c.action do |_gopts, opts, _args|
|
55
|
+
puts @dev.system.time.now.strftime opts[:strftime].to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Configurate time settings'
|
60
|
+
command :time do |c|
|
61
|
+
c.flag :"set-ntp", arg_name: 'host'
|
62
|
+
c.flag :"set-ntp-port", type: Integer, arg_name: 'port'
|
63
|
+
c.flag :"set-mode", must_match: %w[manual ntp], arg_name: 'ntp,manual'
|
64
|
+
c.switch :ntp, negatable: false
|
65
|
+
c.switch :"ntp-port", negatable: false
|
66
|
+
c.switch :mode, negatable: false
|
67
|
+
|
68
|
+
c.action do |_gopts, opts, _args|
|
69
|
+
ntp_edited = false
|
70
|
+
%i[set-ntp set-ntp-port].each do |key|
|
71
|
+
if opts[key]
|
72
|
+
ntp_edited = true
|
73
|
+
break
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
time =@dev.system.time
|
78
|
+
ntp = time.ntp
|
79
|
+
|
80
|
+
print_kv :ntp, ntp.host, opts
|
81
|
+
print_kv :"ntp-port", ntp.port, opts
|
82
|
+
|
83
|
+
ntp.host = opts[:"set-ntp"] if opts[:"set-ntp"]
|
84
|
+
ntp.port = opts[:"set-ntp-port"] if opts[:"set-ntp-port"]
|
85
|
+
|
86
|
+
ntp.update if ntp_edited
|
87
|
+
|
88
|
+
edited = false
|
89
|
+
[:"set-mode"].each do |key|
|
90
|
+
if opts[key]
|
91
|
+
edited = true
|
92
|
+
break
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
print_kv :mode, time.mode.to_s.downcase, opts
|
97
|
+
|
98
|
+
time.mode = opts[:"set-mode"] == 'ntp' ? :NTP : :manual if opts[:"set-mode"]
|
99
|
+
|
100
|
+
time.update if edited
|
101
|
+
|
102
|
+
if ntp_edited and time.mode == :manual
|
103
|
+
puts 'ntp settings altered but the device time mode is set to manual. please run with "--set-mode ntp" to set the time mode to ntp'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
command :model do |c|
|
109
|
+
c.action do
|
110
|
+
puts @dev.system.model
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
command :serial do |c|
|
115
|
+
c.action do
|
116
|
+
puts @dev.system.serial
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
desc 'Shows the device id'
|
121
|
+
command :id do |c|
|
122
|
+
c.action do
|
123
|
+
puts @dev.system.id
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
desc 'Shows the device description'
|
128
|
+
command :desc do |c|
|
129
|
+
c.action do
|
130
|
+
puts @dev.system.description
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
command :mac do |c|
|
135
|
+
c.action do
|
136
|
+
puts @dev.system.mac_address
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
command :reboot do |c|
|
141
|
+
c.action do
|
142
|
+
@dev.system.reboot
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
desc 'List available channels'
|
147
|
+
command :channels do |c|
|
148
|
+
c.action do
|
149
|
+
puts @dev.streaming.channels.collect { |ch| ch.id }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
desc 'Dumps device diagnosed data'
|
154
|
+
command :dumpdata do |c|
|
155
|
+
c.action do
|
156
|
+
puts @dev.system.diagnosed_data
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
command :channel do |c|
|
161
|
+
c.flag :'save-picture', arg_name: 'file', desc: 'saves picture to file'
|
162
|
+
c.flag :'set-video-codec', desc: 'sets channel video codec'
|
163
|
+
c.switch :'video-codec', negatable: false, desc: 'prints channel video codec'
|
164
|
+
c.flag :'set-audio-codec', desc: 'sets channel audio codec'
|
165
|
+
c.switch :'audio-codec', negatable: false, desc: 'prints channel audio codec'
|
166
|
+
c.switch :'disable-audio', negatable: false, desc: 'disables channel audio'
|
167
|
+
c.switch :'enable-audio', negatable: false, desc: 'enabled channel audio'
|
168
|
+
c.switch :'video-resolution', negatable: false, desc: 'prints channel video resolution'
|
169
|
+
c.switch :'video-framerate', negatable: false, desc: 'prints channel video framerate'
|
170
|
+
c.switch :'video-keyframe-interval', negatable: false, desc: 'prints channel keyframe interval'
|
171
|
+
c.switch :'video-cbr', negatable: false, desc: 'prints channel video constant bitrate value'
|
172
|
+
c.switch :'video-bitrate-type', negatable: false, desc: 'prints channel video bitrate type'
|
173
|
+
c.switch :'video-scan-type', negatable: false, desc: 'prints channel video scan type'
|
174
|
+
c.switch :'video-enabled', negatable: false, desc: 'prints if channel video is enabled'
|
175
|
+
c.switch :'audio-enabled', negatable: false, desc: 'prints if channel audio is enabled'
|
176
|
+
c.switch :enabled, negatable: false, desc: 'prints if channel is enabled'
|
177
|
+
c.switch :name, negatable: false, desc: 'prints channel name'
|
178
|
+
c.flag :'set-name', desc: 'sets channel name'
|
179
|
+
c.switch :all, negatable: false, desc: 'prints all possible information'
|
180
|
+
c.switch :'with-keys', negatable: false, desc: 'prints values with keys'
|
181
|
+
|
182
|
+
c.action do |_gopts, opts, args|
|
183
|
+
exit_now! "missing id\nusage: channel <id> [opts]", 1 if args.length != 1
|
184
|
+
id = args[0]
|
185
|
+
exit_now! 'channel id must be an integer', 1 if id.to_i.to_s != id
|
186
|
+
id = id.to_i
|
187
|
+
ch = @dev.streaming.channel(id)
|
188
|
+
exit_now! "channel id doesn't exists", 1 if ch.nil?
|
189
|
+
|
190
|
+
set_opts = %i[set-name set-video-codec set-audio-codec disable-audio enable-audio]
|
191
|
+
|
192
|
+
if opts[:all]
|
193
|
+
opts.each do |key|
|
194
|
+
next if (%w[with-keys save-picture] + set_opts.map { |o| o.to_s }).include? key[0].to_s
|
195
|
+
|
196
|
+
opts[key[0]] = true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
if opts[:'save-picture']
|
201
|
+
file = File.open(opts[:'save-picture'], 'w+b')
|
202
|
+
file.write(ch.picture)
|
203
|
+
file.close
|
204
|
+
end
|
205
|
+
|
206
|
+
print_kv :name, ch.name, opts
|
207
|
+
print_kv :enabled, ch.enabled?, opts
|
208
|
+
print_kv :'video-enabled', ch.video_enabled?, opts
|
209
|
+
print_kv :'video-codec', ch.video_codec, opts
|
210
|
+
print_kv :'video-framerate', ch.video_framerate, opts
|
211
|
+
print_kv :'video-resolution', ch.video_resolution.collect { |r| r.to_s }.join('x'), opts
|
212
|
+
print_kv :'video-keyframe-interval', ch.video_keyframe_interval, opts
|
213
|
+
print_kv :'video-bitrate-type', ch.video_bitrate_type, opts
|
214
|
+
print_kv :'video-cbr', ch.video_cbitrate, opts
|
215
|
+
print_kv :'video-scan-type', ch.video_scan_type, opts
|
216
|
+
print_kv :'audio-enabled', ch.audio_enabled?, opts
|
217
|
+
print_kv :'audio-codec', ch.audio_codec, opts
|
218
|
+
|
219
|
+
edited = false
|
220
|
+
set_opts.each do |k|
|
221
|
+
next unless opts[k]
|
222
|
+
|
223
|
+
edited = true
|
224
|
+
break
|
225
|
+
end
|
226
|
+
|
227
|
+
ch.name = opts[:'set-name'] if opts[:'set-name']
|
228
|
+
set_if_in_opts(ch, :video_codec=, opts[:'set-video-codec'], ch.video_codec_opts)
|
229
|
+
set_if_in_opts(ch, :audio_codec=, opts[:'set-audio-codec'], ch.audio_codec_opts)
|
230
|
+
set_if_in_opts(ch, :audio_enabled=, false, [false]) if opts[:'disable-audio']
|
231
|
+
set_if_in_opts(ch, :audio_enabled=, true, [true]) if opts[:'enable-audio']
|
232
|
+
|
233
|
+
ch.update if edited
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
exit App.run(ARGV)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
private
|
5
|
+
|
6
|
+
def add_xml(method, url_path)
|
7
|
+
iv = :"@#{method}_xml"
|
8
|
+
load_method = :"load_#{method}"
|
9
|
+
define_method load_method do |options = {}|
|
10
|
+
return instance_variable_get(iv) if options.fetch(:cache, true) && instance_variable_defined?(iv)
|
11
|
+
|
12
|
+
url = url_path.respond_to?(:call) ? instance_exec(&url_path) : url_path
|
13
|
+
instance_variable_set(iv, @isapi.get_xml(url, options))
|
14
|
+
end
|
15
|
+
|
16
|
+
reload_method = method == :base ? :reload : :"reload_#{method}"
|
17
|
+
define_method reload_method do |options = {}|
|
18
|
+
send(load_method, options.merge(cache: false))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_getter(method, xml_method, path, opts = { cache: true }, &block)
|
23
|
+
define_method method do
|
24
|
+
v = send(:"load_#{xml_method}", opts).at_xpath(path).inner_html
|
25
|
+
v = block.call(v) if block
|
26
|
+
v
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_list_getter(method, xml_method, path, opts = { cache: true }, &block)
|
31
|
+
define_method method do
|
32
|
+
send(:"load_#{xml_method}", opts).xpath(path).map do |v|
|
33
|
+
v = v.inner_html
|
34
|
+
v = block.call(v) if block
|
35
|
+
v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_opt_getter(method, xml_method, path, transform = nil, &block)
|
41
|
+
define_method method do
|
42
|
+
send(:"load_#{xml_method}", cache: true).at_xpath(path)[:opt].split(',').map do |v|
|
43
|
+
v = v.send(transform) if transform
|
44
|
+
v = block.call(v) if block
|
45
|
+
v
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_opt_range_getter(method, xml_method, path)
|
51
|
+
define_method method do
|
52
|
+
data = send(:"load_#{xml_method}", cache: true).at_xpath(path)
|
53
|
+
data[:min].to_i..data[:max].to_i
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_bool_getter(method, xml_method, path)
|
58
|
+
add_getter(method, xml_method, path) { |v| v == 'true' }
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_setter(method, xml_method, path, *types, &block)
|
62
|
+
update_method = xml_method == :base ? :update : :"update_#{xml_method}"
|
63
|
+
|
64
|
+
if not respond_to? update_method
|
65
|
+
define_method update_method do |options = {}|
|
66
|
+
send(:"before_#{update_method}") if respond_to? :"before_#{update_method}"
|
67
|
+
|
68
|
+
options[:body] = instance_variable_get(:"@#{xml_method}_xml").to_s
|
69
|
+
@isapi.put_xml(respond_to?(:url) ? send(:url) : self.class.url, options)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
define_method method do |value|
|
74
|
+
raise TypeError, "#{method}#{value} (#{value.class}) must be of type #{types}" unless types.any? { |k| value.is_a?(k) }
|
75
|
+
|
76
|
+
value = block.call(value) if block
|
77
|
+
send(:"load_#{xml_method}", cache: true).at_xpath(path).inner_html = value.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Hikvision
|
5
|
+
class ResponseError < RuntimeError
|
6
|
+
def initialize(xml)
|
7
|
+
@xml = xml
|
8
|
+
super(status_string)
|
9
|
+
end
|
10
|
+
|
11
|
+
def status_code
|
12
|
+
@xml.at_xpath('ResponseStatus/statusCode').inner_html.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def status_string
|
16
|
+
@xml.at_xpath('ResponseStatus/statusString').inner_html
|
17
|
+
end
|
18
|
+
|
19
|
+
def sub_status_code
|
20
|
+
@xml.at_xpath('ResponseStatus/subStatusCode').inner_html
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class ISAPI
|
25
|
+
include HTTParty
|
26
|
+
|
27
|
+
attr_accessor :cache
|
28
|
+
attr_reader :streaming, :system
|
29
|
+
|
30
|
+
def initialize(ip, username, password, args = { auth_type: 'digest_auth', https: false })
|
31
|
+
@cache = {}
|
32
|
+
@auth_type = args[:auth_type]
|
33
|
+
@base_uri = "http#{args[:https] ? 's' : ''}://#{ip}"
|
34
|
+
@auth = { username: username, password: password }
|
35
|
+
@streaming = Hikvision::Streaming.new(self)
|
36
|
+
@system = Hikvision::System.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def get(path, options = {})
|
40
|
+
options = default_request_options.merge(options)
|
41
|
+
if @cache.has_key?(path) and options.fetch(:cache, true)
|
42
|
+
@cache[path]
|
43
|
+
else
|
44
|
+
@cache[path] = self.class.get(@base_uri + path, options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_original(path, options = {})
|
49
|
+
options = default_request_options.merge(options)
|
50
|
+
if @cache.has_key?(path) and options.fetch(:cache, true)
|
51
|
+
@cache[path]
|
52
|
+
else
|
53
|
+
@cache[path] = self.class.get(path, options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_xml(path, options = {})
|
58
|
+
data = get(path, options)
|
59
|
+
raise "could not get xml of #{path} code:#{data.response.code}" unless ['200'].include?(data.response.code)
|
60
|
+
|
61
|
+
Nokogiri::XML(data.body).remove_namespaces!
|
62
|
+
end
|
63
|
+
|
64
|
+
def put(path, options = {})
|
65
|
+
@cache.delete(path)
|
66
|
+
options = default_request_options.merge(options)
|
67
|
+
self.class.put(@base_uri + path, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
def post(path, options = {})
|
71
|
+
@cache.delete(path)
|
72
|
+
options = default_request_options.merge(options)
|
73
|
+
self.class.post(@base_uri + path, options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def put_xml(path, options = {})
|
77
|
+
data = put(path, options)
|
78
|
+
|
79
|
+
return true if data.response.code == '200'
|
80
|
+
|
81
|
+
xml = Nokogiri::XML(data.body).remove_namespaces!
|
82
|
+
raise ResponseError, xml
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def default_request_options
|
88
|
+
{
|
89
|
+
"#{@auth_type}": @auth
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class Streaming
|
3
|
+
class Channel < Hikvision::Base
|
4
|
+
def initialize(isapi, xml)
|
5
|
+
@isapi = isapi
|
6
|
+
@base_xml = xml
|
7
|
+
end
|
8
|
+
|
9
|
+
add_xml(:base, -> { url })
|
10
|
+
add_xml(:capabilities, -> { "#{url}/capabilities" })
|
11
|
+
|
12
|
+
add_getter(:id, :base, '//id', &:to_i)
|
13
|
+
|
14
|
+
add_getter(:name, :base, '//channelName')
|
15
|
+
add_setter(:name=, :base, '//channelName', String)
|
16
|
+
add_opt_range_getter(:name_length_opts, :capabilities, '//channelName')
|
17
|
+
|
18
|
+
add_getter(:max_packet_size, :base, '//Transport/maxPacketSize', &:to_i)
|
19
|
+
|
20
|
+
add_getter(:auth_type, :base, '//Transport/Security/certificateType')
|
21
|
+
add_setter(:auth_type=, :base, '//Transport/Security/certificateType', String)
|
22
|
+
add_opt_getter(:auth_type_opts, :capabilities, '//Transport/Security/certificateType', :to_s)
|
23
|
+
|
24
|
+
add_getter(:video_framerate, :base, '//Video/maxFrameRate') { |v| v.to_f / 100 }
|
25
|
+
add_setter(:video_framerate=, :base, '//Video/maxFrameRate', Numeric) { |v| (v * 100).to_i }
|
26
|
+
add_opt_getter(:video_framerate_opts, :capabilities, '//Video/maxFrameRate', :to_f) { |v| v / 100 }
|
27
|
+
|
28
|
+
add_getter(:video_width, :base, '//videoResolutionWidth', &:to_i)
|
29
|
+
add_setter(:video_width=, :base, '//videoResolutionWidth', Integer)
|
30
|
+
add_opt_getter(:video_width_opts, :capabilities, '//videoResolutionWidth', :to_i)
|
31
|
+
|
32
|
+
add_getter(:video_height, :base, '//videoResolutionHeight', &:to_i)
|
33
|
+
add_setter(:video_height=, :base, '//videoResolutionHeight', Integer)
|
34
|
+
add_opt_getter(:video_height_opts, :capabilities, '//videoResolutionHeight', :to_i)
|
35
|
+
|
36
|
+
add_getter(:video_cbitrate, :base, '//Video/constantBitRate', &:to_i)
|
37
|
+
add_setter(:video_cbitrate=, :base, '//Video/constantBitRate', Integer)
|
38
|
+
add_opt_range_getter(:video_cbitrate_opts, :capabilities, '//Video/constantBitRate')
|
39
|
+
|
40
|
+
add_getter(:video_vbitrate_upper_cap, :base, '//Video/vbrUpperCap', &:to_i)
|
41
|
+
add_setter(:video_vbitrate_upper_cap=, :base, '//Video/vbrUpperCap', Integer)
|
42
|
+
add_opt_range_getter(:video_vbitrate_upper_cap_opts, :capabilities, '//Video/vbrUpperCap')
|
43
|
+
|
44
|
+
add_getter(:video_keyframe_interval, :base, '//Video/keyFrameInterval') { |v| v.to_i / 1000 }
|
45
|
+
add_setter(:video_keyframe_interval=, :base, '//Video/keyFrameInterval', Numeric) { |v| (v * 1000).to_i }
|
46
|
+
add_opt_range_getter(:video_keyframe_interval_opts, :capabilities, '//Video/keyFrameInterval')
|
47
|
+
|
48
|
+
add_getter(:video_codec, :base, '//videoCodecType')
|
49
|
+
add_setter(:video_codec=, :base, '//videoCodecType', String)
|
50
|
+
add_opt_getter(:video_codec_opts, :capabilities, '//videoCodecType', :to_s)
|
51
|
+
|
52
|
+
add_getter(:video_bitrate_type, :base, '//videoQualityControlType')
|
53
|
+
add_setter(:video_bitrate_type=, :base, '//videoQualityControlType', String)
|
54
|
+
add_opt_getter(:video_bitrate_type_opts, :capabilities, '//videoQualityControlType', :to_s)
|
55
|
+
|
56
|
+
add_getter(:video_scan_type, :base, '//videoScanType')
|
57
|
+
add_setter(:video_scan_type=, :base, '//videoScanType', String)
|
58
|
+
add_opt_getter(:video_scan_type_opts, :capabilities, '//videoScanType', :to_s)
|
59
|
+
|
60
|
+
add_getter(:snapshot_image_type, :base, '//snapShotImageType')
|
61
|
+
add_setter(:snapshot_image_type=, :base, '//snapShotImageType', String)
|
62
|
+
add_opt_getter(:snapshot_image_type_opts, :capabilities, '//snapShotImageType', :to_s)
|
63
|
+
|
64
|
+
add_getter(:audio_codec, :base, '//audioCompressionType')
|
65
|
+
add_setter(:audio_codec=, :base, '//audioCompressionType', String)
|
66
|
+
add_opt_getter(:audio_codec_opts, :capabilities, '//audioCompressionType', :to_s)
|
67
|
+
|
68
|
+
add_getter(:video_smoothing, :base, '//Video/smoothing', &:to_i)
|
69
|
+
add_opt_range_getter(:video_smoothing_opts, :capabilities, '//Video/smoothing')
|
70
|
+
|
71
|
+
add_bool_getter(:audio_enabled?, :base, '//Audio/enabled')
|
72
|
+
add_setter(:audio_enabled=, :base, '//Audio/enabled', TrueClass, FalseClass)
|
73
|
+
|
74
|
+
add_bool_getter(:svc_enabled?, :base, '//Video/SVC/enabled')
|
75
|
+
add_setter(:svc_enabled=, :base, '//Video/SVC/enabled', TrueClass, FalseClass)
|
76
|
+
|
77
|
+
add_bool_getter(:enabled?, :base, '/StreamingChannel/enabled')
|
78
|
+
add_bool_getter(:video_enabled?, :base, '//Video/enabled')
|
79
|
+
add_bool_getter(:multicast_enabled?, :base, '//Transport/Multicast/enabled')
|
80
|
+
add_bool_getter(:unicast_enabled?, :base, '//Transport/Unicast/enabled')
|
81
|
+
add_bool_getter(:security_enabled?, :base, '//Transport/Security/enabled')
|
82
|
+
|
83
|
+
def video_resolution
|
84
|
+
[video_width, video_height]
|
85
|
+
end
|
86
|
+
|
87
|
+
def video_resolution=(value)
|
88
|
+
video_width = value[0]
|
89
|
+
video_height = value[1]
|
90
|
+
end
|
91
|
+
|
92
|
+
def video_resolution_opts
|
93
|
+
video_width_opts.zip(video_height_opts)
|
94
|
+
end
|
95
|
+
|
96
|
+
def picture(options = { cache: false })
|
97
|
+
@isapi.get("#{url}/picture", options).response.body
|
98
|
+
end
|
99
|
+
|
100
|
+
def url
|
101
|
+
"/ISAPI/Streaming/channels/#{id}"
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def before_update
|
107
|
+
@isapi.cache.delete('/ISAPI/Streaming/channels')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class Streaming
|
3
|
+
def initialize(isapi)
|
4
|
+
@isapi = isapi
|
5
|
+
end
|
6
|
+
|
7
|
+
def channels
|
8
|
+
load_channels.values
|
9
|
+
end
|
10
|
+
|
11
|
+
def channel(id)
|
12
|
+
load_channels[id]
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_channels(options = {})
|
16
|
+
return @channels if options.fetch(:cache, true) && instance_variable_defined?(:@channels)
|
17
|
+
|
18
|
+
@channels = {}
|
19
|
+
xml = @isapi.get_xml('/ISAPI/Streaming/channels', options)
|
20
|
+
xml.xpath('StreamingChannelList/StreamingChannel').each do |c|
|
21
|
+
channel = Channel.new(@isapi, Nokogiri::XML(c.to_s))
|
22
|
+
@channels[channel.id] = channel
|
23
|
+
end
|
24
|
+
@channels
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class System
|
3
|
+
def get_event(options = { cache: false })
|
4
|
+
@isapi.post('/ISAPI/AccessControl/AcsEvent?format=json', options).response.body
|
5
|
+
end
|
6
|
+
|
7
|
+
# 保存刷脸照片
|
8
|
+
def get_event_photo(url, file_path, options = { cache: false })
|
9
|
+
file_content = @isapi.get_original(url, options).response.body
|
10
|
+
File.open(file_path, 'wb') do |f|
|
11
|
+
f.write(file_content)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class System
|
3
|
+
add_xml(:device_info, '/ISAPI/System/deviceInfo')
|
4
|
+
|
5
|
+
add_getter(:name, :device_info, '//deviceName')
|
6
|
+
add_getter(:id, :device_info, '//deviceID')
|
7
|
+
add_getter(:description, :device_info, '//deviceDescription')
|
8
|
+
add_getter(:location, :device_info, '//deviceLocation')
|
9
|
+
add_getter(:model, :device_info, '//model')
|
10
|
+
add_getter(:serial, :device_info, '//serialNumber')
|
11
|
+
add_getter(:mac_address, :device_info, '//macAddress')
|
12
|
+
add_getter(:firmware_version, :device_info, '//firmwareVersion')
|
13
|
+
add_getter(:encoder_version, :device_info, '//encoderVersion')
|
14
|
+
add_getter(:boot_version, :device_info, '//bootVersion')
|
15
|
+
add_getter(:hardware_version, :device_info, '//hardwareVersion')
|
16
|
+
add_getter(:type, :device_info, '//deviceType')
|
17
|
+
|
18
|
+
add_bool_getter(:support_beep?, :device_info, '/DeviceInfo/supportBeep')
|
19
|
+
add_bool_getter(:support_video_loss?, :device_info, '/DeviceInfo/supportVideoLoss')
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class Network
|
3
|
+
class Integration < Hikvision::Base
|
4
|
+
class << self
|
5
|
+
def url
|
6
|
+
"/ISAPI/System/Network/Integrate"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(isapi)
|
11
|
+
@isapi = isapi
|
12
|
+
end
|
13
|
+
|
14
|
+
add_xml(:base, url)
|
15
|
+
|
16
|
+
add_setter(:cgi=, :base, '//CGI/enable', TrueClass, FalseClass)
|
17
|
+
|
18
|
+
add_getter(:cgi_authentication, :base, '//CGI/certificateType') { |v| v.include?("basic") ? :basic : :digest }
|
19
|
+
add_setter(:cgi_authentication=, :base, '//CGI/certificateType', Symbol) { |v| v == :basic ? "digest/basic" : v.to_s }
|
20
|
+
|
21
|
+
add_bool_getter(:cgi?, :base, '//CGI/enable')
|
22
|
+
add_bool_getter(:onvif?, :base, '//ONVIF/enable')
|
23
|
+
add_bool_getter(:isapi?, :base, '//ISAPI/enable')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class System
|
3
|
+
def add_person(options = { cache: false })
|
4
|
+
@isapi.post('/ISAPI/AccessControl/UserInfo/Record?format=json', options).response.body
|
5
|
+
end
|
6
|
+
|
7
|
+
def delete_person(options = { cache: false })
|
8
|
+
@isapi.put('/ISAPI/AccessControl/UserInfo/Delete?format=json', options).response.body
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class System
|
3
|
+
add_xml(:status, '/ISAPI/System/status')
|
4
|
+
|
5
|
+
add_getter(:uptime, :status, '//deviceUpTime', { cache: false }, &:to_i)
|
6
|
+
|
7
|
+
add_list_getter(:memory_usage, :status, '//memoryUsage', { cache: false }, &:to_i)
|
8
|
+
add_list_getter(:memory_available, :status, '//memoryAvailable', { cache: false }, &:to_i)
|
9
|
+
add_list_getter(:memory_description, :status, '//memoryDescription')
|
10
|
+
add_list_getter(:cpu_utilization, :status, '//cpuUtilization', { cache: false }, &:to_i)
|
11
|
+
add_list_getter(:cpu_description, :status, '//cpuDescription')
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Hikvision
|
2
|
+
class System
|
3
|
+
class Time
|
4
|
+
class Ntp < Hikvision::Base
|
5
|
+
class << self
|
6
|
+
def url
|
7
|
+
'/ISAPI/System/time/ntpServers'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(isapi)
|
12
|
+
@isapi = isapi
|
13
|
+
end
|
14
|
+
|
15
|
+
add_xml(:base, url)
|
16
|
+
|
17
|
+
add_getter(:host, :base, '//hostName')
|
18
|
+
add_setter(:host=, :base, '//hostName', String)
|
19
|
+
|
20
|
+
add_getter(:sync_interval, :base, '//synchronizeInterval', &:to_i)
|
21
|
+
add_setter(:sync_interval=, :base, '//synchronizeInterval', Integer)
|
22
|
+
|
23
|
+
add_getter(:port, :base, '//portNo', &:to_i)
|
24
|
+
add_setter(:port=, :base, '//portNo', Integer)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Hikvision
|
4
|
+
class System
|
5
|
+
class Time < Hikvision::Base
|
6
|
+
attr_reader :ntp
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def url
|
10
|
+
'/ISAPI/System/time'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(isapi)
|
15
|
+
@isapi = isapi
|
16
|
+
@ntp = Ntp.new(isapi)
|
17
|
+
end
|
18
|
+
|
19
|
+
add_xml(:base, url)
|
20
|
+
|
21
|
+
add_getter(:mode, :base, '//timeMode', &:to_sym)
|
22
|
+
add_setter(:mode=, :base, '//timeMode', Symbol, String)
|
23
|
+
|
24
|
+
add_getter(:now, :base, '//localTime', cache: false) { |v| Object::Time.strptime(v, '%Y-%m-%dT%H:%M:%S%Z') }
|
25
|
+
add_setter(:now=, :base, '//localTime', Object::Time) { |v| v.strftime('%Y-%m-%dT%H:%M:%S%Z') }
|
26
|
+
|
27
|
+
add_getter(:zone, :base, '//timeZone')
|
28
|
+
add_setter(:zone=, :base, '//timeZone', String)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/hikvision.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'hikvision/version'
|
2
|
+
require_relative 'hikvision/base'
|
3
|
+
require_relative 'hikvision/isapi'
|
4
|
+
require_relative 'hikvision/streaming'
|
5
|
+
require_relative 'hikvision/streaming/channel'
|
6
|
+
require_relative 'hikvision/system'
|
7
|
+
require_relative 'hikvision/system/time'
|
8
|
+
require_relative 'hikvision/system/time/ntp'
|
9
|
+
require_relative 'hikvision/system/device_info'
|
10
|
+
require_relative 'hikvision/system/network'
|
11
|
+
require_relative 'hikvision/system/network/integration'
|
12
|
+
require_relative 'hikvision/system/reboot'
|
13
|
+
require_relative 'hikvision/system/diagnosed_data'
|
14
|
+
require_relative 'hikvision/system/status'
|
15
|
+
require_relative 'hikvision/system/acs_event'
|
16
|
+
require_relative 'hikvision/system/face'
|
17
|
+
require_relative 'hikvision/system/person'
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hik_ISAPI
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- guopipi
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gli
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.21'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.21'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.21'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.21'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
executables:
|
58
|
+
- hikvision
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- exe/hikvision
|
63
|
+
- lib/hikvision.rb
|
64
|
+
- lib/hikvision/base.rb
|
65
|
+
- lib/hikvision/isapi.rb
|
66
|
+
- lib/hikvision/streaming.rb
|
67
|
+
- lib/hikvision/streaming/channel.rb
|
68
|
+
- lib/hikvision/system.rb
|
69
|
+
- lib/hikvision/system/acs_event.rb
|
70
|
+
- lib/hikvision/system/device_info.rb
|
71
|
+
- lib/hikvision/system/diagnosed_data.rb
|
72
|
+
- lib/hikvision/system/face.rb
|
73
|
+
- lib/hikvision/system/network.rb
|
74
|
+
- lib/hikvision/system/network/integration.rb
|
75
|
+
- lib/hikvision/system/person.rb
|
76
|
+
- lib/hikvision/system/reboot.rb
|
77
|
+
- lib/hikvision/system/status.rb
|
78
|
+
- lib/hikvision/system/time.rb
|
79
|
+
- lib/hikvision/system/time/ntp.rb
|
80
|
+
- lib/hikvision/version.rb
|
81
|
+
homepage: https://github.com/guojhq/hik_ISAPI.git
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubygems_version: 3.2.33
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: Ruby Hikvision ISAPI Interface
|
104
|
+
test_files: []
|