alula-ruby 2.6.3 → 2.8.0
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 +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'
|