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: []
|