alula-ruby 2.6.3 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION.md +2 -0
- data/bin/console +0 -1
- data/lib/alula/dcp/error_handler.rb +29 -0
- data/lib/alula/dcp/list_object.rb +72 -0
- data/lib/alula/dcp/meta.rb +18 -0
- data/lib/alula/dcp/resource_attributes.rb +194 -0
- data/lib/alula/dcp_operations/delete.rb +29 -0
- data/lib/alula/dcp_operations/delete_staged.rb +25 -0
- data/lib/alula/dcp_operations/list.rb +34 -0
- data/lib/alula/dcp_operations/request.rb +32 -0
- data/lib/alula/dcp_operations/save.rb +47 -0
- data/lib/alula/dcp_resource.rb +238 -0
- data/lib/alula/errors.rb +16 -6
- data/lib/alula/helpers/device_helpers/program_id_helper.rb +9 -0
- data/lib/alula/resource_attributes.rb +5 -5
- data/lib/alula/resources/dcp/base_resource.rb +43 -0
- data/lib/alula/resources/dcp/config/synchronize.rb +53 -0
- data/lib/alula/resources/dcp/config.rb +8 -0
- data/lib/alula/resources/dcp/users_data/installer_pin.rb +21 -0
- data/lib/alula/resources/dcp/users_data/user.rb +100 -0
- data/lib/alula/resources/dcp/users_data.rb +13 -0
- data/lib/alula/resources/device.rb +1 -1
- data/lib/alula/singleton_dcp_command_resource.rb +100 -0
- data/lib/alula/util.rb +24 -7
- data/lib/alula/version.rb +1 -1
- data/lib/alula.rb +18 -1
- metadata +19 -2
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alula
|
4
|
+
# Parent class for all DCP objects
|
5
|
+
class DcpResource
|
6
|
+
attr_accessor :device_id, :index, :raw_data, :meta, :errors, :rate_limit, :dirty_attributes, :value, :staged
|
7
|
+
|
8
|
+
def self.class_name
|
9
|
+
name.split('::')[-1]
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(device_id, index = nil, attributes = {})
|
13
|
+
@raw_data = {}
|
14
|
+
@dirty_attributes = {}
|
15
|
+
@errors = ModelErrors.new(self.class)
|
16
|
+
construct_from(device_id, index, attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def clone
|
20
|
+
self.class.new(@device_id, nil, @raw_data)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Construct a new resource, ready to receive attributes, with
|
25
|
+
# empty values for all attrs.
|
26
|
+
# Useful for making New resources
|
27
|
+
def self.build(device_id)
|
28
|
+
fields = get_fields.keys.map { |k| Util.camelize(k) }
|
29
|
+
empty_shell = fields.each_with_object({}) { |f, obj| obj[f] = nil }
|
30
|
+
new(device_id, nil, empty_shell)
|
31
|
+
end
|
32
|
+
|
33
|
+
def construct_from(device_id, index, json_object)
|
34
|
+
raise ArgumentError, 'device_id is required' if device_id.empty?
|
35
|
+
|
36
|
+
@raw_data = json_object.dup
|
37
|
+
assign_meta_value_staged(json_object)
|
38
|
+
self.index = index
|
39
|
+
self.device_id = device_id
|
40
|
+
|
41
|
+
@errors = ModelErrors.new(self.class)
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Take a hash of attributes and apply them to the model
|
48
|
+
def apply_attributes(attributes)
|
49
|
+
attributes.each do |key, value|
|
50
|
+
next unless fields.include?(key)
|
51
|
+
|
52
|
+
@dirty_attributes[key] = value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def reconstruct_from(device_id, index, json_object)
|
57
|
+
construct_from(device_id, index, json_object)
|
58
|
+
end
|
59
|
+
|
60
|
+
def errors?
|
61
|
+
@errors.any?
|
62
|
+
end
|
63
|
+
|
64
|
+
def refresh
|
65
|
+
response = Alula::Client.request(:get, resource_url, {}, {})
|
66
|
+
if response.ok?
|
67
|
+
model = construct_from(device_id, index, response.data)
|
68
|
+
model.rate_limit = response.rate_limit
|
69
|
+
model
|
70
|
+
else
|
71
|
+
error_class = AlulaError.for_response(response)
|
72
|
+
raise error_class
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Fetch known attributes out of the object, collected into a Hash in camelCase format
|
78
|
+
# Intended for eventually making its way back up to the API
|
79
|
+
def as_json
|
80
|
+
field_names.each_with_object({}) do |ruby_key, obj|
|
81
|
+
key = Util.camelize(ruby_key)
|
82
|
+
val = find_value(ruby_key)
|
83
|
+
|
84
|
+
next if val.nil?
|
85
|
+
|
86
|
+
obj[key] = if val.is_a? Array
|
87
|
+
val.map { |v| parse_value(v, ruby_key) }
|
88
|
+
elsif val.is_a? Alula::Dcp::ObjectField
|
89
|
+
val.as_json
|
90
|
+
else
|
91
|
+
parse_value(val, ruby_key)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_value(key)
|
97
|
+
# prioritize dirty attributes, then staged, then value
|
98
|
+
# ideally we should use only dirty attributes, done by executing .apply_attributes
|
99
|
+
return @dirty_attributes[key] if @dirty_attributes.include?(key)
|
100
|
+
return @staged.send(key) if @staged.respond_to?(key)
|
101
|
+
|
102
|
+
@value.send(key) if @value.respond_to?(key)
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Reduce as_json to a set that can be updated
|
107
|
+
def as_patchable_json
|
108
|
+
values = as_json.each_pair.each_with_object({}) do |(key, val), collector|
|
109
|
+
collector[key] = val
|
110
|
+
end
|
111
|
+
|
112
|
+
values.reject { |k, _v| %w[index version].include? k } # blacklist index and version
|
113
|
+
end
|
114
|
+
|
115
|
+
def annotate_errors(model_errors)
|
116
|
+
@errors = ModelErrors.new(self.class)
|
117
|
+
model_errors.each_pair do |field_name, error|
|
118
|
+
errors.add(field_name, error)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Return an instance of QueryEngine annotated with the correct model attributes
|
124
|
+
def filter_builder
|
125
|
+
Alula::FilterBuilder.new(self.class)
|
126
|
+
end
|
127
|
+
alias fb filter_builder
|
128
|
+
|
129
|
+
def model_name
|
130
|
+
self.class
|
131
|
+
end
|
132
|
+
|
133
|
+
# Class for holding model errors
|
134
|
+
class ModelErrors
|
135
|
+
include Enumerable
|
136
|
+
|
137
|
+
def initialize(model_class)
|
138
|
+
@model_class = model_class
|
139
|
+
@details = {}
|
140
|
+
end
|
141
|
+
|
142
|
+
def each
|
143
|
+
@details.each do |field, error|
|
144
|
+
yield({ field => error })
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def any?
|
149
|
+
@details.any?
|
150
|
+
end
|
151
|
+
|
152
|
+
def add(field_name, error_message)
|
153
|
+
@details[field_name] = error_message
|
154
|
+
end
|
155
|
+
|
156
|
+
def [](field_name)
|
157
|
+
@details[field_name.to_sym]
|
158
|
+
end
|
159
|
+
|
160
|
+
def full_messages
|
161
|
+
@details.map { |field, error| "#{field}: #{error}" }
|
162
|
+
end
|
163
|
+
|
164
|
+
def full_messages_for(attribute_name)
|
165
|
+
return nil unless @details[attribute_name.to_sym].present?
|
166
|
+
|
167
|
+
"#{attribute_name}: #{@details[attribute_name.to_sym]}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def assign_meta_value_staged(json_object)
|
174
|
+
@meta = Alula::Dcp::Meta.new(json_object['meta']) unless empty_value?(json_object['meta'])
|
175
|
+
@value = convert_to_ruby_object(json_object['value'])
|
176
|
+
@staged = convert_to_ruby_object(json_object['staged'])
|
177
|
+
end
|
178
|
+
|
179
|
+
def convert_to_ruby_object(object)
|
180
|
+
return nil if object.nil?
|
181
|
+
|
182
|
+
fields.each_pair.each_with_object({}) do |(field_name, opts), obj|
|
183
|
+
value = object[Util.camelize(field_name.to_s)]
|
184
|
+
if value.nil?
|
185
|
+
# we still need to define the method, even if it's nil
|
186
|
+
obj[field_name] = nil
|
187
|
+
obj.define_singleton_method(field_name) { obj[field_name] }
|
188
|
+
next
|
189
|
+
end
|
190
|
+
|
191
|
+
obj[field_name] =
|
192
|
+
if opts[:type] == :date && ![nil, ''].include?(value)
|
193
|
+
begin
|
194
|
+
DateTime.parse(value)
|
195
|
+
rescue ArgumentError
|
196
|
+
value
|
197
|
+
end
|
198
|
+
elsif opts[:type] == :object && opts[:use] && !value.nil? && value.respond_to?(:each)
|
199
|
+
opts[:use].new(field_name, value)
|
200
|
+
elsif opts[:type] == :array && opts[:use] && !value.nil? && value.respond_to?(:each)
|
201
|
+
# Array of objects, each object is a new instance of the use class
|
202
|
+
value.each_with_object([]) do |item, collector|
|
203
|
+
collector << opts[:use].new(field_name, item)
|
204
|
+
end
|
205
|
+
elsif opts[:type] == :boolean
|
206
|
+
[true, 'true', 1, '1'].include? value
|
207
|
+
elsif opts[:type] == :number
|
208
|
+
value&.to_i
|
209
|
+
else
|
210
|
+
value
|
211
|
+
end
|
212
|
+
obj.define_singleton_method(field_name) { obj[field_name] }
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def parse_value(value, ruby_key)
|
217
|
+
if date_fields.include?(ruby_key) && ![nil, ''].include?(value)
|
218
|
+
extract_date(value)
|
219
|
+
elsif value.is_a? Alula::Dcp::ObjectField
|
220
|
+
value.as_json
|
221
|
+
else
|
222
|
+
value
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def extract_date(value)
|
227
|
+
if value.respond_to? :strftime
|
228
|
+
value.strftime('%Y-%m-%dT%H:%M:%S.%L%z')
|
229
|
+
else
|
230
|
+
value.to_s
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def empty_value?(value)
|
235
|
+
[nil, ''].include?(value)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
data/lib/alula/errors.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alula
|
2
4
|
class AlulaError < StandardError
|
3
5
|
attr_reader :http_status, :raw_response, :error, :message
|
6
|
+
|
4
7
|
def initialize(error)
|
5
8
|
if error.class == String
|
6
9
|
@message = error
|
@@ -24,9 +27,12 @@ module Alula
|
|
24
27
|
elsif !response.data['errors'].nil? && !response.data['errors'].empty?
|
25
28
|
self.errors_for_response(response)
|
26
29
|
|
30
|
+
elsif !response.data['message'].nil?
|
31
|
+
self.error_for_response(response)
|
32
|
+
|
27
33
|
elsif response.data.match(/^<!DOCTYPE html>/)
|
28
34
|
self.critical_error_for_response(response.data.scan(/<pre>(.*)<\/pre>/))
|
29
|
-
|
35
|
+
|
30
36
|
elsif response.data.match(/502 Bad Gateway/)
|
31
37
|
self.gateway_error_for_response(response.data.scan(/502 Bad Gateway/))
|
32
38
|
else
|
@@ -34,7 +40,6 @@ module Alula
|
|
34
40
|
Alula.logger.error message
|
35
41
|
raise UnknownApiError.new(message)
|
36
42
|
end
|
37
|
-
|
38
43
|
rescue NoMethodError
|
39
44
|
message = "Unable to derive error from response: #{response.inspect}"
|
40
45
|
Alula.logger.error message
|
@@ -66,6 +71,10 @@ module Alula
|
|
66
71
|
when 'server_error'
|
67
72
|
Alula.logger.error response
|
68
73
|
ServerError.new(response)
|
74
|
+
when 'Bad Request'
|
75
|
+
BadRequestError.new(response)
|
76
|
+
when 'Not Found'
|
77
|
+
NotFoundError.new(response)
|
69
78
|
else
|
70
79
|
#
|
71
80
|
# RPC errors are identified by jsonrpc in the body.
|
@@ -77,7 +86,8 @@ module Alula
|
|
77
86
|
# Here we have some errors that are actually known but at least one of them is not properly formatted by the API
|
78
87
|
message = response.data.dig('error', 'data', 'msg') ||
|
79
88
|
response.data.dig('error', 'data', 'message') ||
|
80
|
-
response.data.dig('error', 'description')
|
89
|
+
response.data.dig('error', 'description') ||
|
90
|
+
response.data['message']
|
81
91
|
case message
|
82
92
|
when 'dealer does not exist'
|
83
93
|
NotFoundError.new(message)
|
@@ -199,14 +209,14 @@ module Alula
|
|
199
209
|
@http_status = response.http_status
|
200
210
|
@raw_response = response
|
201
211
|
@error = error['message']
|
202
|
-
@full_messages = error.dig('data', 'message')&.split(', ') ||
|
203
|
-
|
212
|
+
@full_messages = error.dig('data', 'message')&.split(', ') ||
|
213
|
+
[error['message']]
|
204
214
|
@message = error['message']
|
205
215
|
@code = error['code']
|
206
216
|
end
|
207
217
|
|
208
218
|
#
|
209
|
-
# Provides interface mirroring to success responses
|
219
|
+
# Provides interface mirroring to success responses
|
210
220
|
def ok?
|
211
221
|
false
|
212
222
|
end
|
@@ -76,6 +76,10 @@ module Alula
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def alder_wtp?
|
79
|
+
[38, 44].include?(program_id)
|
80
|
+
end
|
81
|
+
|
82
|
+
def alder_wtp_01?
|
79
83
|
program_id == 38
|
80
84
|
end
|
81
85
|
|
@@ -98,6 +102,11 @@ module Alula
|
|
98
102
|
def xip_family?
|
99
103
|
self.class::XIP_FAMILY_PROGRAM_IDS.include?(program_id)
|
100
104
|
end
|
105
|
+
|
106
|
+
def supports_onvif?
|
107
|
+
# C+Pro-SC, BAT-Mini, BAT-Mini AV
|
108
|
+
[42, 39, 43].include?(program_id)
|
109
|
+
end
|
101
110
|
end
|
102
111
|
end
|
103
112
|
end
|
@@ -52,11 +52,11 @@ module Alula
|
|
52
52
|
@fields[field_name] = opts
|
53
53
|
|
54
54
|
self.instance_eval do
|
55
|
-
|
55
|
+
json_key = Util.camelize(field_name)
|
56
56
|
|
57
57
|
# Reader method for attribute
|
58
58
|
define_method(field_name) do
|
59
|
-
value = @values[
|
59
|
+
value = @values[json_key]
|
60
60
|
|
61
61
|
if opts[:type] == :date && ![nil, ''].include?(value)
|
62
62
|
begin
|
@@ -72,7 +72,7 @@ module Alula
|
|
72
72
|
# API sends a camelCase string; provide symbol to Client
|
73
73
|
value ? Util.underscore(value).to_sym : nil
|
74
74
|
elsif opts[:hex_convert_required] == true
|
75
|
-
Util.convert_hex_crc?(
|
75
|
+
Util.convert_hex_crc?(program_id) ? value.to_s(16) : value
|
76
76
|
elsif opts[:type] == :number
|
77
77
|
value&.to_i
|
78
78
|
else
|
@@ -96,10 +96,10 @@ module Alula
|
|
96
96
|
end
|
97
97
|
|
98
98
|
# Mark the attribute as dirty if the new value is different
|
99
|
-
mark_dirty(field_name, @values[
|
99
|
+
mark_dirty(field_name, @values[json_key], new_value)
|
100
100
|
#
|
101
101
|
# Assign the new value (always assigned even if a duplicate)
|
102
|
-
@values[
|
102
|
+
@values[json_key] = new_value
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alula
|
4
|
+
module Dcp
|
5
|
+
# Base class for DCP resources
|
6
|
+
class BaseResource < Alula::DcpResource
|
7
|
+
class << self
|
8
|
+
def api_name(name = nil)
|
9
|
+
if name
|
10
|
+
@api_name = name
|
11
|
+
elsif @api_name
|
12
|
+
@api_name
|
13
|
+
else
|
14
|
+
superclass.api_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Infer resource name from classname if not provided
|
19
|
+
def resource_name(name = nil)
|
20
|
+
if name
|
21
|
+
@resource_name = name
|
22
|
+
elsif @resource_name
|
23
|
+
@resource_name
|
24
|
+
else
|
25
|
+
@resource_name = self.name.split('::').last.downcase.to_sym
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def resource_url(device_id, index = nil)
|
30
|
+
base_url = "/#{api_name}/v2/helix/#{device_id}/#{resource_name}"
|
31
|
+
index ? "#{base_url}/#{index}" : base_url
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
api_name :dcp
|
36
|
+
|
37
|
+
# Instance method
|
38
|
+
def resource_url(device_id = self.device_id, index = self.index)
|
39
|
+
"/#{self.class.api_name}/v2/helix/#{device_id}/#{self.class.resource_name}/#{index}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alula
|
4
|
+
module Dcp
|
5
|
+
class Config
|
6
|
+
# Synchronize DCP configuration
|
7
|
+
class Synchronize < Alula::SingletonDcpCommandResource
|
8
|
+
extend Alula::Dcp::ResourceAttributes
|
9
|
+
|
10
|
+
resource_path 'config/synchronize'
|
11
|
+
|
12
|
+
field :alarm_types_supported, type: :boolean
|
13
|
+
field :arming_level_options_by_arming_level, type: :boolean
|
14
|
+
field :bus_modules, type: :boolean
|
15
|
+
field :comm_data, type: :boolean
|
16
|
+
field :custom_zone_profiles, type: :boolean
|
17
|
+
field :fob_data, type: :boolean
|
18
|
+
field :local_scene_actions, type: :boolean
|
19
|
+
field :local_scene_names, type: :boolean
|
20
|
+
field :local_scene_triggers, type: :boolean
|
21
|
+
field :local_scenes, type: :boolean
|
22
|
+
field :misc_nv_data, type: :boolean
|
23
|
+
field :wireless_peripherals, type: :boolean
|
24
|
+
field :mobile_device_names, type: :boolean
|
25
|
+
field :mobile_device_settings, type: :boolean
|
26
|
+
field :outputs, type: :boolean
|
27
|
+
field :panel_options, type: :boolean
|
28
|
+
field :partitions, type: :boolean
|
29
|
+
field :pin_pad_data, type: :boolean
|
30
|
+
field :pinger_fob_data, type: :boolean
|
31
|
+
field :siren_data, type: :boolean
|
32
|
+
field :time_schedules, type: :boolean
|
33
|
+
field :timers, type: :boolean
|
34
|
+
field :user_mapping, type: :boolean
|
35
|
+
field :users_data, type: :boolean
|
36
|
+
field :zone_data, type: :boolean
|
37
|
+
field :zwave_device_names, type: :boolean
|
38
|
+
|
39
|
+
def self.call(device_id:, payload:)
|
40
|
+
request(
|
41
|
+
device_id: device_id,
|
42
|
+
http_method: :post,
|
43
|
+
payload: payload
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.payload_required?
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alula
|
4
|
+
module Dcp
|
5
|
+
class UsersData
|
6
|
+
# Panel User Resource
|
7
|
+
class InstallerPin < Alula::Dcp::BaseResource
|
8
|
+
extend Alula::Dcp::ResourceAttributes
|
9
|
+
extend Alula::DcpOperations::Request
|
10
|
+
extend Alula::DcpOperations::Save
|
11
|
+
extend Alula::DcpOperations::Delete
|
12
|
+
extend Alula::DcpOperations::DeleteStaged
|
13
|
+
|
14
|
+
resource_name 'config/usersData/installerPin'
|
15
|
+
|
16
|
+
field :primitive,
|
17
|
+
type: :string
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alula
|
4
|
+
module Dcp
|
5
|
+
class UsersData
|
6
|
+
# Panel User Resource
|
7
|
+
class User < Alula::Dcp::BaseResource
|
8
|
+
extend Alula::Dcp::ResourceAttributes
|
9
|
+
extend Alula::DcpOperations::Request
|
10
|
+
extend Alula::DcpOperations::Save
|
11
|
+
extend Alula::DcpOperations::Delete
|
12
|
+
extend Alula::DcpOperations::DeleteStaged
|
13
|
+
|
14
|
+
resource_name 'config/usersData/user'
|
15
|
+
|
16
|
+
# Partition class to be used as an array field on the User class
|
17
|
+
class Partition < Alula::Dcp::ObjectField
|
18
|
+
field :assigned,
|
19
|
+
type: :boolean
|
20
|
+
|
21
|
+
field :bypass_zones,
|
22
|
+
type: :boolean
|
23
|
+
|
24
|
+
field :manage_users,
|
25
|
+
type: :boolean
|
26
|
+
|
27
|
+
field :system_test,
|
28
|
+
type: :boolean
|
29
|
+
|
30
|
+
field :view_history,
|
31
|
+
type: :boolean
|
32
|
+
|
33
|
+
field :control_outputs,
|
34
|
+
type: :boolean
|
35
|
+
|
36
|
+
field :manage_scenes,
|
37
|
+
type: :boolean
|
38
|
+
|
39
|
+
field :manage_zwave_devices,
|
40
|
+
type: :boolean
|
41
|
+
|
42
|
+
field :silence_trouble_beeps,
|
43
|
+
type: :boolean
|
44
|
+
|
45
|
+
field :zone_sensor_reset,
|
46
|
+
type: :boolean
|
47
|
+
|
48
|
+
field :master_user,
|
49
|
+
type: :boolean
|
50
|
+
|
51
|
+
field :confirm_alarms,
|
52
|
+
type: :boolean
|
53
|
+
|
54
|
+
field :control_chime_mode,
|
55
|
+
type: :boolean
|
56
|
+
|
57
|
+
field :level1,
|
58
|
+
type: :boolean
|
59
|
+
|
60
|
+
field :level2,
|
61
|
+
type: :boolean
|
62
|
+
|
63
|
+
field :level3,
|
64
|
+
type: :boolean
|
65
|
+
|
66
|
+
field :level4,
|
67
|
+
type: :boolean
|
68
|
+
|
69
|
+
field :level5,
|
70
|
+
type: :boolean
|
71
|
+
|
72
|
+
field :level6,
|
73
|
+
type: :boolean
|
74
|
+
|
75
|
+
field :level7,
|
76
|
+
type: :boolean
|
77
|
+
|
78
|
+
field :level8,
|
79
|
+
type: :boolean
|
80
|
+
end
|
81
|
+
|
82
|
+
field :display_name,
|
83
|
+
type: :string
|
84
|
+
|
85
|
+
field :user_fob_number,
|
86
|
+
type: :number
|
87
|
+
|
88
|
+
field :user_pin,
|
89
|
+
type: :string
|
90
|
+
|
91
|
+
field :partitions,
|
92
|
+
type: :array,
|
93
|
+
use: Partition
|
94
|
+
|
95
|
+
field :version,
|
96
|
+
type: :number
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alula
|
4
|
+
module Dcp
|
5
|
+
# Main class for UsersData
|
6
|
+
# Subclasses: UsersData::User, UsersData::DuressPin, UsersData::InstallerPin, UsersData::AccessCodeUserMapping
|
7
|
+
class UsersData < Alula::Dcp::BaseResource
|
8
|
+
extend Alula::DcpOperations::List
|
9
|
+
|
10
|
+
resource_name 'config/usersData'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -24,7 +24,7 @@ module Alula
|
|
24
24
|
field :video_verification, type: :boolean
|
25
25
|
field :sms_notification, type: :boolean
|
26
26
|
field :panel_downloading, type: :boolean
|
27
|
-
|
27
|
+
field :onvif_video_hub, type: :boolean
|
28
28
|
end
|
29
29
|
|
30
30
|
resource_path 'devices'
|