ruby_home 0.2.0 → 0.2.5

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +41 -0
  3. data/CHANGELOG.md +13 -0
  4. data/README.md +47 -84
  5. data/examples/air_purifier.rb +67 -0
  6. data/examples/air_quality_sensor.rb +120 -0
  7. data/examples/battery_service.rb +59 -0
  8. data/examples/carbon_dioxide_sensor.rb +79 -0
  9. data/examples/carbon_monoxide_sensor.rb +79 -0
  10. data/examples/contact_sensor.rb +67 -0
  11. data/examples/door.rb +49 -0
  12. data/examples/fan.rb +31 -0
  13. data/examples/fan_v2.rb +69 -0
  14. data/examples/garage_door_opener.rb +72 -0
  15. data/examples/heater_cooler.rb +93 -0
  16. data/examples/humidifier_dehumidifier.rb +89 -0
  17. data/examples/humidity_sensor.rb +63 -0
  18. data/examples/leak_sensor.rb +67 -0
  19. data/examples/light_sensor.rb +65 -0
  20. data/examples/lightbulb.rb +32 -0
  21. data/examples/lock_mechanism.rb +35 -0
  22. data/examples/motion_sensor.rb +67 -0
  23. data/examples/occupancy_sensor.rb +67 -0
  24. data/examples/outlet.rb +30 -0
  25. data/examples/security_system.rb +54 -0
  26. data/examples/smoke_sensor.rb +67 -0
  27. data/examples/switch.rb +17 -0
  28. data/examples/television.rb +78 -0
  29. data/examples/temperature_sensor.rb +63 -0
  30. data/examples/thermostat.rb +114 -0
  31. data/examples/window.rb +49 -0
  32. data/examples/window_covering.rb +73 -0
  33. data/lib/ruby_home.rb +0 -1
  34. data/lib/ruby_home/characteristic.rb +7 -1
  35. data/lib/ruby_home/config/categories.yml +39 -0
  36. data/lib/ruby_home/config/characteristics.yml +479 -3
  37. data/lib/ruby_home/config/services.yml +79 -4
  38. data/lib/ruby_home/configuration.rb +19 -0
  39. data/lib/ruby_home/dns/service.rb +2 -0
  40. data/lib/ruby_home/dns/text_record.rb +1 -1
  41. data/lib/ruby_home/errors.rb +4 -0
  42. data/lib/ruby_home/factories/characteristic_factory.rb +1 -0
  43. data/lib/ruby_home/factories/templates/characteristic_template.rb +1 -1
  44. data/lib/ruby_home/factories/templates/service_template.rb +1 -1
  45. data/lib/ruby_home/hap/server_handler.rb +4 -13
  46. data/lib/ruby_home/hap/values/uint8_value.rb +11 -1
  47. data/lib/ruby_home/http/controllers/application_controller.rb +10 -3
  48. data/lib/ruby_home/http/controllers/pair_setups_controller.rb +3 -3
  49. data/lib/ruby_home/http/controllers/pair_verifies_controller.rb +10 -12
  50. data/lib/ruby_home/http/controllers/pairings_controller.rb +1 -1
  51. data/lib/ruby_home/http/serializers/characteristic_serializer.rb +13 -5
  52. data/lib/ruby_home/http/serializers/service_serializer.rb +11 -1
  53. data/lib/ruby_home/http/services/verify_finish_service.rb +56 -0
  54. data/lib/ruby_home/http/services/verify_srp_service.rb +32 -41
  55. data/lib/ruby_home/persistable.rb +4 -12
  56. data/lib/ruby_home/service.rb +3 -0
  57. data/lib/ruby_home/version.rb +1 -1
  58. data/rubyhome.gemspec +7 -7
  59. data/sbin/service_generator.rb +12 -2
  60. metadata +65 -39
  61. data/.travis.yml +0 -31
@@ -152,6 +152,14 @@
152
152
  - 00000023-0000-1000-8000-0026BB765291
153
153
  :required_characteristics_uuids:
154
154
  - 000000AC-0000-1000-8000-0026BB765291
155
+ - :name: :faucet
156
+ :description: Faucet
157
+ :uuid: 000000D7-0000-1000-8000-0026BB765291
158
+ :optional_characteristics_uuids:
159
+ - 00000023-0000-1000-8000-0026BB765291
160
+ - 00000077-0000-1000-8000-0026BB765291
161
+ :required_characteristics_uuids:
162
+ - 000000B0-0000-1000-8000-0026BB765291
155
163
  - :name: :garage_door_opener
156
164
  :description: Garage Door Opener
157
165
  :uuid: 00000041-0000-1000-8000-0026BB765291
@@ -206,6 +214,30 @@
206
214
  - 00000023-0000-1000-8000-0026BB765291
207
215
  :required_characteristics_uuids:
208
216
  - 00000010-0000-1000-8000-0026BB765291
217
+ - :name: :input_source
218
+ :description: Input Source
219
+ :uuid: 000000D9-0000-1000-8000-0026BB765291
220
+ :optional_characteristics_uuids:
221
+ - 000000E6-0000-1000-8000-0026BB765291
222
+ - 000000DC-0000-1000-8000-0026BB765291
223
+ - 00000134-0000-1000-8000-0026BB765291
224
+ :required_characteristics_uuids:
225
+ - 000000E3-0000-1000-8000-0026BB765291
226
+ - 000000DB-0000-1000-8000-0026BB765291
227
+ - 000000D6-0000-1000-8000-0026BB765291
228
+ - 00000023-0000-1000-8000-0026BB765291
229
+ - 00000135-0000-1000-8000-0026BB765291
230
+ - :name: :irrigation_system
231
+ :description: Irrigation System
232
+ :uuid: 000000CF-0000-1000-8000-0026BB765291
233
+ :optional_characteristics_uuids:
234
+ - 00000023-0000-1000-8000-0026BB765291
235
+ - 000000D4-0000-1000-8000-0026BB765291
236
+ - 00000077-0000-1000-8000-0026BB765291
237
+ :required_characteristics_uuids:
238
+ - 000000B0-0000-1000-8000-0026BB765291
239
+ - 000000D1-0000-1000-8000-0026BB765291
240
+ - 000000D2-0000-1000-8000-0026BB765291
209
241
  - :name: :leak_sensor
210
242
  :description: Leak Sensor
211
243
  :uuid: '00000083-0000-1000-8000-0026BB765291'
@@ -221,11 +253,11 @@
221
253
  :description: Light Sensor
222
254
  :uuid: '00000084-0000-1000-8000-0026BB765291'
223
255
  :optional_characteristics_uuids:
256
+ - 00000023-0000-1000-8000-0026BB765291
224
257
  - 00000075-0000-1000-8000-0026BB765291
225
258
  - 00000077-0000-1000-8000-0026BB765291
226
259
  - 0000007A-0000-1000-8000-0026BB765291
227
260
  - '00000079-0000-1000-8000-0026BB765291'
228
- - 00000023-0000-1000-8000-0026BB765291
229
261
  :required_characteristics_uuids:
230
262
  - 0000006B-0000-1000-8000-0026BB765291
231
263
  - :name: :lightbulb
@@ -236,6 +268,7 @@
236
268
  - 00000013-0000-1000-8000-0026BB765291
237
269
  - 0000002F-0000-1000-8000-0026BB765291
238
270
  - 00000023-0000-1000-8000-0026BB765291
271
+ - 000000CE-0000-1000-8000-0026BB765291
239
272
  :required_characteristics_uuids:
240
273
  - 00000025-0000-1000-8000-0026BB765291
241
274
  - :name: :lock_management
@@ -265,9 +298,9 @@
265
298
  :description: Microphone
266
299
  :uuid: 00000112-0000-1000-8000-0026BB765291
267
300
  :optional_characteristics_uuids:
268
- - '00000119-0000-1000-8000-0026BB765291'
269
301
  - 00000023-0000-1000-8000-0026BB765291
270
302
  :required_characteristics_uuids:
303
+ - '00000119-0000-1000-8000-0026BB765291'
271
304
  - 0000011A-0000-1000-8000-0026BB765291
272
305
  - :name: :motion_sensor
273
306
  :description: Motion Sensor
@@ -284,11 +317,11 @@
284
317
  :description: Occupancy Sensor
285
318
  :uuid: '00000086-0000-1000-8000-0026BB765291'
286
319
  :optional_characteristics_uuids:
320
+ - 00000023-0000-1000-8000-0026BB765291
287
321
  - 00000075-0000-1000-8000-0026BB765291
288
322
  - 00000077-0000-1000-8000-0026BB765291
289
323
  - 0000007A-0000-1000-8000-0026BB765291
290
324
  - '00000079-0000-1000-8000-0026BB765291'
291
- - 00000023-0000-1000-8000-0026BB765291
292
325
  :required_characteristics_uuids:
293
326
  - 00000071-0000-1000-8000-0026BB765291
294
327
  - :name: :outlet
@@ -343,8 +376,8 @@
343
376
  :description: Speaker
344
377
  :uuid: 00000113-0000-1000-8000-0026BB765291
345
378
  :optional_characteristics_uuids:
346
- - '00000119-0000-1000-8000-0026BB765291'
347
379
  - 00000023-0000-1000-8000-0026BB765291
380
+ - '00000119-0000-1000-8000-0026BB765291'
348
381
  :required_characteristics_uuids:
349
382
  - 0000011A-0000-1000-8000-0026BB765291
350
383
  - :name: :stateless_programmable_switch
@@ -362,6 +395,34 @@
362
395
  - 00000023-0000-1000-8000-0026BB765291
363
396
  :required_characteristics_uuids:
364
397
  - 00000025-0000-1000-8000-0026BB765291
398
+ - :name: :television
399
+ :description: Television
400
+ :uuid: '000000D8-0000-1000-8000-0026BB765291'
401
+ :optional_characteristics_uuids:
402
+ - 00000008-0000-1000-8000-0026BB765291
403
+ - 000000DD-0000-1000-8000-0026BB765291
404
+ - 00000136-0000-1000-8000-0026BB765291
405
+ - 000000E0-0000-1000-8000-0026BB765291
406
+ - 00000137-0000-1000-8000-0026BB765291
407
+ - 000000E2-0000-1000-8000-0026BB765291
408
+ - 000000DF-0000-1000-8000-0026BB765291
409
+ - 00000023-0000-1000-8000-0026BB765291
410
+ :required_characteristics_uuids:
411
+ - 000000B0-0000-1000-8000-0026BB765291
412
+ - 000000E7-0000-1000-8000-0026BB765291
413
+ - 000000E3-0000-1000-8000-0026BB765291
414
+ - 000000E1-0000-1000-8000-0026BB765291
415
+ - 000000E8-0000-1000-8000-0026BB765291
416
+ - :name: :television_speaker
417
+ :description: Television Speaker
418
+ :uuid: 00000113-0000-1000-8000-0026BB765291
419
+ :optional_characteristics_uuids:
420
+ - 000000B0-0000-1000-8000-0026BB765291
421
+ - 00000119-0000-1000-8000-0026BB765291
422
+ - 000000E9-0000-1000-8000-0026BB765291
423
+ - 000000EA-0000-1000-8000-0026BB765291
424
+ :required_characteristics_uuids:
425
+ - 0000011A-0000-1000-8000-0026BB765291
365
426
  - :name: :temperature_sensor
366
427
  :description: Temperature Sensor
367
428
  :uuid: '0000008A-0000-1000-8000-0026BB765291'
@@ -388,6 +449,20 @@
388
449
  - 00000011-0000-1000-8000-0026BB765291
389
450
  - 00000035-0000-1000-8000-0026BB765291
390
451
  - 00000036-0000-1000-8000-0026BB765291
452
+ - :name: :valve
453
+ :description: Valve
454
+ :uuid: 000000D0-0000-1000-8000-0026BB765291
455
+ :optional_characteristics_uuids:
456
+ - 000000D3-0000-1000-8000-0026BB765291
457
+ - 000000D4-0000-1000-8000-0026BB765291
458
+ - 000000D6-0000-1000-8000-0026BB765291
459
+ - 000000CB-0000-1000-8000-0026BB765291
460
+ - 00000077-0000-1000-8000-0026BB765291
461
+ - 00000023-0000-1000-8000-0026BB765291
462
+ :required_characteristics_uuids:
463
+ - 000000B0-0000-1000-8000-0026BB765291
464
+ - 000000D2-0000-1000-8000-0026BB765291
465
+ - 000000D5-0000-1000-8000-0026BB765291
391
466
  - :name: :window
392
467
  :description: Window
393
468
  :uuid: '0000008B-0000-1000-8000-0026BB765291'
@@ -1,7 +1,12 @@
1
1
  module RubyHome
2
+ class UnknownCategoriyIdentifierError < StandardError; end
3
+
2
4
  class Configuration
3
5
  extend Forwardable
4
6
 
7
+ CATEGORIES_FILEPATH = (File.dirname(__FILE__) + '/config/categories.yml').freeze
8
+ CATEGORIES = YAML.load_file(CATEGORIES_FILEPATH).freeze
9
+
5
10
  def initialize(accessory_info = AccessoryInfo.instance)
6
11
  @accessory_info = accessory_info
7
12
  end
@@ -11,6 +16,7 @@ module RubyHome
11
16
  DEFAULT_PORT = 4567
12
17
  DEFAULT_MODEL_NAME = DEFAULT_NAME
13
18
  DEFAULT_DISCOVERY_NAME = DEFAULT_NAME
19
+ DEFAULT_CATEGORY_IDENTIFIER = 2
14
20
 
15
21
  def discovery_name
16
22
  @discovery_name || DEFAULT_DISCOVERY_NAME
@@ -28,6 +34,19 @@ module RubyHome
28
34
  @host || DEFAULT_HOST
29
35
  end
30
36
 
37
+ def category_identifier
38
+ @category_identifier || DEFAULT_CATEGORY_IDENTIFIER
39
+ end
40
+
41
+ def category_identifier=(value)
42
+ if value.is_a?(Symbol)
43
+ raise UnknownCategoriyIdentifierError if !CATEGORIES.include?(value)
44
+ @category_identifier = CATEGORIES[value]
45
+ else
46
+ @category_identifier = value.to_i
47
+ end
48
+ end
49
+
31
50
  attr_writer :discovery_name, :model_name, :host, :port
32
51
 
33
52
  def_delegators :@accessory_info, :password, :password=
@@ -6,6 +6,8 @@ module RubyHome
6
6
  end
7
7
 
8
8
  def update
9
+ return if RbConfig::CONFIG['target_os'] =~ /linux/
10
+
9
11
  dnssd_service.add_record(DNSSD::Record::TXT, text_record.encode)
10
12
  end
11
13
 
@@ -38,7 +38,7 @@ module RubyHome
38
38
  # 1-65535.
39
39
 
40
40
  def accessory_category_identifier
41
- 2
41
+ @configuration.category_identifier
42
42
  end
43
43
 
44
44
  # Feature flags (e.g. "0x3" for bits 0 and 1). Required if non-zero.
@@ -0,0 +1,4 @@
1
+ module RubyHome
2
+ class RubyHomeError < StandardError; end
3
+ class UnknownValueError < RubyHomeError; end
4
+ end
@@ -15,6 +15,7 @@ module RubyHome
15
15
  format: template.format,
16
16
  name: characteristic_name,
17
17
  properties: template.properties,
18
+ constraints: template.constraints,
18
19
  service: service,
19
20
  unit: template.unit,
20
21
  uuid: template.uuid,
@@ -4,7 +4,7 @@ module RubyHome
4
4
  DATA = YAML.load_file(FILEPATH).freeze
5
5
 
6
6
  def self.all
7
- @@all ||= DATA.map { |data| new(data) }
7
+ @@all ||= DATA.map { |data| new(**data) }
8
8
  end
9
9
 
10
10
  def self.find_by(options)
@@ -4,7 +4,7 @@ module RubyHome
4
4
  DATA = YAML.load_file(FILEPATH).freeze
5
5
 
6
6
  def self.all
7
- @all ||= DATA.map { |data| new(data) }
7
+ @all ||= DATA.map { |data| new(**data) }
8
8
  end
9
9
 
10
10
  def self.find_by(options)
@@ -35,29 +35,20 @@ module RubyHome
35
35
  Host: bind_address,
36
36
  ServerSoftware: 'RubyHome',
37
37
  Logger: server_logger,
38
- AccessLog: access_logger
38
+ AccessLog: []
39
39
  }
40
40
  end
41
41
 
42
42
  def server_logger
43
- if ENV['DEBUG']
43
+ if ENV['DEBUG'] == 'debug'
44
44
  WEBrick::Log::new(STDOUT, WEBrick::BasicLog::DEBUG)
45
+ elsif ENV['DEBUG'] == 'info'
46
+ WEBrick::Log::new(STDOUT, WEBrick::BasicLog::INFO)
45
47
  else
46
48
  WEBrick::Log::new("/dev/null", WEBrick::BasicLog::WARN)
47
49
  end
48
50
  end
49
51
 
50
- def access_logger
51
- if ENV['DEBUG']
52
- [
53
- [ STDOUT, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
54
- [ STDOUT, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
55
- ]
56
- else
57
- []
58
- end
59
- end
60
-
61
52
  def bind_address
62
53
  configuration.host
63
54
  end
@@ -13,7 +13,17 @@ module RubyHome
13
13
  end
14
14
 
15
15
  def valid_values
16
- template.constraints.fetch('ValidValues')
16
+ defined_values || range_values || raise(UnknownValueError, "Constraint contains an unrecognized list of values: #{template.constraints.inspect }")
17
+ end
18
+
19
+ def defined_values
20
+ template.constraints.dig('ValidValues')
21
+ end
22
+
23
+ def range_values
24
+ if min = template.constraints.dig('MinimumValue')
25
+ { min.to_s => min }
26
+ end
17
27
  end
18
28
  end
19
29
  end
@@ -5,10 +5,17 @@ module RubyHome
5
5
  class ApplicationController < Sinatra::Base
6
6
  disable :protection
7
7
 
8
- if ENV['DEBUG']
9
- enable :logging
10
- set :logger, Logger.new(STDOUT)
8
+ logger = Logger.new(STDOUT)
9
+ logger.level = case ENV['DEBUG']
10
+ when 'debug'
11
+ Logger::DEBUG
12
+ when 'info'
13
+ Logger::INFO
14
+ else
15
+ Logger::WARN
11
16
  end
17
+ set :logger, logger
18
+ enable :logging if ENV['DEBUG']
12
19
 
13
20
  before do
14
21
  logger.debug "Session"
@@ -38,9 +38,9 @@ module RubyHome
38
38
  device_proof: unpack_request[:proof],
39
39
  srp_session: session.srp_session,
40
40
  public_key: unpack_request[:public_key],
41
- )
41
+ ).run
42
42
 
43
- if verify_srp.valid?
43
+ if verify_srp.success?
44
44
  session.session_key = verify_srp.session_key
45
45
  session.srp_session = nil
46
46
 
@@ -96,7 +96,7 @@ module RubyHome
96
96
  encrypted_data = chacha20poly1305ietf.encrypt(nonce, subtlv)
97
97
 
98
98
  pairing_params = { admin: true, identifier: iosdevicepairingid, public_key: iosdeviceltpk.unpack1('H*') }
99
- accessory_info.add_paired_client pairing_params
99
+ accessory_info.add_paired_client(**pairing_params)
100
100
 
101
101
  tlv state: 6, encrypted_data: encrypted_data
102
102
  end
@@ -48,18 +48,16 @@ module RubyHome
48
48
  end
49
49
 
50
50
  def verify_finish_response
51
- encrypted_data = unpack_request[:encrypted_data]
52
-
53
- chacha20poly1305ietf = HAP::Crypto::ChaCha20Poly1305.new(session.session_key)
54
- nonce = HexHelper.pad('PV-Msg03')
55
- decrypted_data = chacha20poly1305ietf.decrypt(nonce, encrypted_data)
56
- unpacked_decrypted_data = TLV.decode(decrypted_data)
57
-
58
- if accessory_info.paired_clients.any? {|h| h[:identifier] == unpacked_decrypted_data[:identifier]}
59
- shared_secret = HAP::Crypto::SessionKey.new(session.shared_secret)
60
-
61
- session.controller_to_accessory_key = shared_secret.controller_to_accessory_key
62
- session.accessory_to_controller_key = shared_secret.accessory_to_controller_key
51
+ verify_finish = VerifyFinishService.new(
52
+ encrypted_data: unpack_request[:encrypted_data],
53
+ session_key: session.session_key,
54
+ shared_secret: session.shared_secret,
55
+ accessory_info: accessory_info
56
+ ).run
57
+
58
+ if verify_finish.success?
59
+ session.controller_to_accessory_key = verify_finish.controller_to_accessory_key
60
+ session.accessory_to_controller_key = verify_finish.accessory_to_controller_key
63
61
 
64
62
  session.session_key = nil
65
63
  session.shared_secret = nil
@@ -22,7 +22,7 @@ module RubyHome
22
22
  identifier: unpack_request[:identifier],
23
23
  public_key: unpack_request[:public_key].unpack1('H*')
24
24
  }
25
- accessory_info.add_paired_client pairing_params
25
+ accessory_info.add_paired_client(**pairing_params)
26
26
 
27
27
  tlv state: 2
28
28
  end
@@ -14,13 +14,13 @@ module RubyHome
14
14
  'perms' => perms(characteristic),
15
15
  'format' => characteristic.format,
16
16
  'description' => characteristic.description,
17
- }.merge(value_hash(characteristic))
17
+ }
18
+ .merge(value_hash(characteristic))
19
+ .merge(valid_values_hash(characteristic))
18
20
  end
19
21
 
20
22
  private
21
23
 
22
- HIDDEN_VALUE_OBJECTS = [ IdentifyValue ].freeze
23
-
24
24
  def perms(characteristic)
25
25
  characteristic.properties.map do |property|
26
26
  RubyHome::Characteristic::PROPERTIES[property]
@@ -28,10 +28,18 @@ module RubyHome
28
28
  end
29
29
 
30
30
  def value_hash(characteristic)
31
- if HIDDEN_VALUE_OBJECTS.include?(characteristic.value_object.class)
31
+ if characteristic.properties.include?('read')
32
+ { 'value' => characteristic.value }
33
+ else
34
+ {}
35
+ end
36
+ end
37
+
38
+ def valid_values_hash(characteristic)
39
+ if characteristic.valid_values.empty?
32
40
  {}
33
41
  else
34
- { 'value' => characteristic.value }.compact
42
+ { 'valid-values' => characteristic.valid_values }
35
43
  end
36
44
  end
37
45
  end
@@ -15,7 +15,17 @@ module RubyHome
15
15
  'characteristics' => CharacteristicSerializer.new(service.characteristics).serializable_hash,
16
16
  'primary' => service.primary,
17
17
  'hidden' => service.hidden,
18
- }
18
+ }.merge(linked_services(service))
19
+ end
20
+
21
+ private
22
+
23
+ def linked_services(service)
24
+ if service.linked.empty?
25
+ {}
26
+ else
27
+ { 'linked' => service.linked.map(&:instance_id) }
28
+ end
19
29
  end
20
30
  end
21
31
  end
@@ -0,0 +1,56 @@
1
+ module RubyHome
2
+ class VerifyFinishService
3
+ def initialize(accessory_info: , encrypted_data: , session_key:, shared_secret:)
4
+ @accessory_info = accessory_info
5
+ @encrypted_data = encrypted_data
6
+ @session_key = session_key
7
+ @shared_secret = HAP::Crypto::SessionKey.new(shared_secret)
8
+ end
9
+
10
+ def run
11
+ if paired_client_exists?
12
+ RubyHome.dns_service.update
13
+
14
+ OpenStruct.new(
15
+ success?: true,
16
+ controller_to_accessory_key: shared_secret.controller_to_accessory_key,
17
+ accessory_to_controller_key: shared_secret.accessory_to_controller_key
18
+ )
19
+ else
20
+ OpenStruct.new(success?: false)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ NONCE = -'PV-Msg03'
27
+
28
+ attr_reader :accessory_info, :encrypted_data, :session_key, :shared_secret
29
+
30
+ def paired_client_exists?
31
+ accessory_info.paired_clients.any? do |paired_client|
32
+ paired_client[:identifier] == identifier
33
+ end
34
+ end
35
+
36
+ def chacha20poly1305
37
+ HAP::Crypto::ChaCha20Poly1305.new(session_key)
38
+ end
39
+
40
+ def nonce
41
+ HexHelper.pad(NONCE)
42
+ end
43
+
44
+ def decrypted_data
45
+ chacha20poly1305.decrypt(nonce, encrypted_data)
46
+ end
47
+
48
+ def unpacked_decrypted_data
49
+ TLV.decode(decrypted_data)
50
+ end
51
+
52
+ def identifier
53
+ unpacked_decrypted_data[:identifier]
54
+ end
55
+ end
56
+ end