ruby_home 0.1.7 → 0.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7f5f53b947e2cfbce79204572dd8d29b284c5434304aee7214b6a7a33d3e0a6
4
- data.tar.gz: '086845819574df45f838c8ff67979f55f01763b6fe44114af76fd3479f8f1b83'
3
+ metadata.gz: dff58e7d6769b9869dd1c7368ced5443698c27197ca89c4f85eafe9320fb1f62
4
+ data.tar.gz: 9ee496a8ebb0a2c7d1693501e5bef6cb14d99cc92d6d70aa34c04ca81947ff33
5
5
  SHA512:
6
- metadata.gz: 419df1283d71010c0a4e8e89f60f01fa752ea9366f6fe59a1ca3a2e202a9ab72361ed4dff5389ecdfb8b084feb40cd0d5c62fcbf16b90b65bfcd5096c7a3a4ab
7
- data.tar.gz: 3644d107b711edadd639f881614ebd85cb8e164cbeebf5db30e1748303983b52bf056371e952a6c9c0e39aa2a7744351549ddaf2eef4c655428dabdf0c9fe2eb
6
+ metadata.gz: a633129cf9f076ec82ed3f24295a1d70b1f2efd405466c3dee2f11ce6c56d85cdfd3fc2b7264cac70987abadf6b87f564f82431b903baae6041bbf734fcbd9f7
7
+ data.tar.gz: fdc5d4de8f61b619acfc20d647f5eb2f3fd68dc062921f2c0dc1305575c5eab5985af69ea2481e14be755721017510578c31135be1bcf7f166ad26348a5a0e35
data/README.md CHANGED
@@ -65,7 +65,7 @@ RubyHome.run
65
65
 
66
66
  ## Customization
67
67
 
68
- RubyHome ties to provide sane defaults for all services. Customization of any of the options is possible.
68
+ RubyHome tries to provide sane defaults for all services. Customization of any of the options is possible.
69
69
 
70
70
  ```ruby
71
71
  require 'ruby_home'
@@ -4,42 +4,51 @@ module RubyHome
4
4
  identify: nil,
5
5
  }.freeze
6
6
 
7
- def self.create(characteristic_name, service: , value: nil)
7
+ def self.create(characteristic_name, service: , subtype: 'default' , value: nil)
8
8
  new(
9
9
  characteristic_name: characteristic_name,
10
10
  service: service,
11
+ subtype: subtype,
11
12
  value: value
12
13
  ).create
13
14
  end
14
15
 
15
16
  def create
16
- service.characteristics << new_characteristic
17
+ characteristic = Characteristic.new(
18
+ description: template.description,
19
+ format: template.format,
20
+ name: characteristic_name,
21
+ properties: template.properties,
22
+ service: service,
23
+ unit: template.unit,
24
+ uuid: template.uuid,
25
+ value: value
26
+ )
27
+
28
+ if persisted_characteristic
29
+ characteristic.instance_id = persisted_characteristic.instance_id
30
+ else
31
+ characteristic.instance_id = accessory.next_available_instance_id
32
+ persist_characteristic(characteristic)
33
+ end
17
34
 
18
- new_characteristic
35
+ service.characteristics << characteristic
36
+
37
+ characteristic
19
38
  end
20
39
 
21
40
  private
22
41
 
23
- def initialize(characteristic_name:, service:, value:)
42
+ def initialize(characteristic_name:, service:, subtype:, value:)
24
43
  @characteristic_name = characteristic_name.to_sym
25
44
  @service = service
45
+ @subtype = subtype
26
46
  @value = value || default_value
27
47
  end
28
48
 
29
- attr_reader :service, :characteristic_name, :value
49
+ attr_reader :service, :characteristic_name, :value, :subtype
30
50
 
31
- def new_characteristic
32
- @new_characteristic ||= Characteristic.new(
33
- description: template.description,
34
- format: template.format,
35
- name: characteristic_name,
36
- properties: template.properties,
37
- service: service,
38
- unit: template.unit,
39
- uuid: template.uuid,
40
- value: value
41
- )
42
- end
51
+ delegate :accessory, to: :service
43
52
 
44
53
  def template
45
54
  @template ||= CharacteristicTemplate.find_by(name: characteristic_name)
@@ -57,5 +66,24 @@ module RubyHome
57
66
  end
58
67
  end
59
68
  end
69
+
70
+ def persisted_characteristic
71
+ IdentifierCache.find_by(
72
+ accessory_id: service.accessory_id,
73
+ service_uuid: service.uuid,
74
+ uuid: template.uuid,
75
+ subtype: subtype
76
+ )
77
+ end
78
+
79
+ def persist_characteristic(characteristic)
80
+ IdentifierCache.create(
81
+ accessory_id: characteristic.accessory_id,
82
+ instance_id: characteristic.instance_id,
83
+ service_uuid: service.uuid,
84
+ uuid: characteristic.uuid,
85
+ subtype: subtype
86
+ )
87
+ end
60
88
  end
61
89
  end
@@ -1,24 +1,43 @@
1
1
  module RubyHome
2
2
  class ServiceFactory
3
- def self.create(service_name, accessory: Accessory.new, **options)
3
+ def self.create(service_name, accessory: Accessory.new, subtype: 'default', **options)
4
4
  new(
5
5
  service_name: service_name,
6
6
  accessory: accessory,
7
+ subtype: subtype,
7
8
  **options
8
9
  ).create
9
10
  end
10
11
 
11
12
  def create
12
- accessory.services << new_service
13
+ service = Service.new(
14
+ accessory: accessory,
15
+ description: template.description,
16
+ name: service_name,
17
+ uuid: template.uuid,
18
+ )
19
+
20
+ if persisted_service
21
+ service.instance_id = persisted_service.instance_id
22
+ else
23
+ service.instance_id = accessory.next_available_instance_id
24
+ persist_service(service)
25
+ end
13
26
 
14
- create_accessory_information
15
- create_required_characteristics
16
- create_optional_characteristics
27
+ accessory.services << service
28
+
29
+ create_required_characteristics(service)
30
+ create_optional_characteristics(service)
31
+
32
+ unless accessory_information_factory?
33
+ create_accessory_information
34
+ end
17
35
 
18
- new_service
36
+ service
19
37
  end
20
38
 
21
39
  private
40
+
22
41
  ACCESSORY_INFORMATION_OPTIONS = [
23
42
  :firmware_revision,
24
43
  :manufacturer,
@@ -27,40 +46,34 @@ module RubyHome
27
46
  :serial_number
28
47
  ].freeze
29
48
 
30
- def initialize(service_name:, accessory:, **options)
49
+ def initialize(service_name:, accessory:, subtype:, **options)
31
50
  @service_name = service_name.to_sym
32
51
  @accessory = accessory
52
+ @subtype = subtype
33
53
  @options = options
34
54
  end
35
55
 
36
- attr_reader :service_name, :accessory, :options
37
-
38
- def new_service
39
- @new_service ||= Service.new(
40
- accessory: accessory,
41
- description: template.description,
42
- name: service_name,
43
- uuid: template.uuid
44
- )
45
- end
56
+ attr_reader :service_name, :accessory, :subtype, :options
46
57
 
47
- def create_required_characteristics
58
+ def create_required_characteristics(service)
48
59
  template.required_characteristics.each do |characteristic_template|
49
60
  characteristic_name = characteristic_template.name
50
61
  CharacteristicFactory.create(
51
62
  characteristic_template.name,
52
- service: new_service,
63
+ service: service,
64
+ subtype: subtype,
53
65
  value: options[characteristic_name]
54
66
  )
55
67
  end
56
68
  end
57
69
 
58
- def create_optional_characteristics
70
+ def create_optional_characteristics(service)
59
71
  optional_characteristic_templates.each do |characteristic_template|
60
72
  characteristic_name = characteristic_template.name
61
73
  CharacteristicFactory.create(
62
74
  characteristic_name,
63
- service: new_service,
75
+ service: service,
76
+ subtype: subtype,
64
77
  value: options[characteristic_name]
65
78
  )
66
79
  end
@@ -72,18 +85,40 @@ module RubyHome
72
85
  end
73
86
  end
74
87
 
88
+ def accessory_information_factory?
89
+ service_name == :accessory_information
90
+ end
91
+
75
92
  def create_accessory_information
76
- unless service_name == :accessory_information
77
- ServiceFactory.create(
78
- :accessory_information,
79
- accessory: accessory,
80
- **options.slice(*ACCESSORY_INFORMATION_OPTIONS)
81
- )
82
- end
93
+ return if accessory.has_accessory_information?
94
+
95
+ ServiceFactory.create(
96
+ :accessory_information,
97
+ accessory: accessory,
98
+ subtype: 'accessory_information',
99
+ **options.slice(*ACCESSORY_INFORMATION_OPTIONS)
100
+ )
83
101
  end
84
102
 
85
103
  def template
86
104
  @template ||= ServiceTemplate.find_by(name: service_name)
87
105
  end
106
+
107
+ def persisted_service
108
+ IdentifierCache.find_by(
109
+ accessory_id: accessory.id,
110
+ uuid: template.uuid,
111
+ subtype: subtype
112
+ )
113
+ end
114
+
115
+ def persist_service(service)
116
+ IdentifierCache.create(
117
+ accessory_id: accessory.id,
118
+ instance_id: service.instance_id,
119
+ uuid: template.uuid,
120
+ subtype: subtype
121
+ )
122
+ end
88
123
  end
89
124
  end
@@ -1,3 +1,5 @@
1
+ require_relative 'accessory_collection'
2
+
1
3
  module RubyHome
2
4
  class Accessory
3
5
  @@all = AccessoryCollection.new
@@ -26,6 +28,16 @@ module RubyHome
26
28
  (largest_instance_id || 0) + 1
27
29
  end
28
30
 
31
+ def has_accessory_information?
32
+ services.any? do |service|
33
+ service.name == :accessory_information
34
+ end
35
+ end
36
+
37
+ def contains_instance_id?(instance_id)
38
+ instance_ids.include?(instance_id)
39
+ end
40
+
29
41
  private
30
42
 
31
43
  def instance_ids
@@ -37,7 +49,7 @@ module RubyHome
37
49
  end
38
50
 
39
51
  def largest_instance_id
40
- instance_ids.max
52
+ IdentifierCache.where(accessory_id: id).map(&:instance_id).max
41
53
  end
42
54
 
43
55
  def next_available_accessory_id
@@ -1,4 +1,6 @@
1
1
  module RubyHome
2
+ class DuplicateCharacteristicError < StandardError; end
3
+
2
4
  class Characteristic
3
5
  include Wisper::Publisher
4
6
 
@@ -30,7 +32,6 @@ module RubyHome
30
32
  @properties = properties
31
33
  @service = service
32
34
  @value = value
33
- @instance_id = accessory.next_available_instance_id
34
35
  end
35
36
 
36
37
  attr_reader(
@@ -45,14 +46,15 @@ module RubyHome
45
46
  :instance_id
46
47
  )
47
48
 
48
- def accessory
49
- service.accessory
50
- end
49
+ def instance_id=(new_id)
50
+ raise DuplicateCharacteristicError if accessory.contains_instance_id?(new_id)
51
51
 
52
- def accessory_id
53
- accessory.id
52
+ @instance_id = new_id
54
53
  end
55
54
 
55
+ delegate :accessory, to: :service
56
+ delegate :id, to: :accessory, prefix: true
57
+
56
58
  def service_iid
57
59
  service.instance_id
58
60
  end
@@ -10,10 +10,10 @@ module RubyHome
10
10
 
11
11
  def send_response(socket)
12
12
  if encryption_time?
13
- response = String.new
13
+ response = StringIO.new
14
14
  super(response)
15
15
 
16
- encrypted_response = encrypter.encrypt(response).join
16
+ encrypted_response = encrypter.encrypt(response.string).join
17
17
  cache[:accessory_to_controller_count] = encrypter.count
18
18
 
19
19
  _write_data(socket, encrypted_response)
@@ -1,4 +1,6 @@
1
1
  module RubyHome
2
+ class DuplicateServiceError < StandardError; end
3
+
2
4
  class Service
3
5
  def initialize(accessory: , primary: false, hidden: false, name:, description:, uuid:)
4
6
  @accessory = accessory
@@ -8,7 +10,6 @@ module RubyHome
8
10
  @description = description
9
11
  @uuid = uuid
10
12
  @characteristics = []
11
- @instance_id = accessory.next_available_instance_id
12
13
  end
13
14
 
14
15
  attr_reader(
@@ -22,6 +23,14 @@ module RubyHome
22
23
  :instance_id
23
24
  )
24
25
 
26
+ def instance_id=(new_id)
27
+ raise DuplicateServiceError if accessory.contains_instance_id?(new_id)
28
+
29
+ @instance_id = new_id
30
+ end
31
+
32
+ delegate :id, to: :accessory, prefix: true
33
+
25
34
  def characteristic(characteristic_name)
26
35
  characteristics.find do |characteristic|
27
36
  characteristic.name == characteristic_name
@@ -17,6 +17,7 @@ module RubyHome
17
17
 
18
18
  map('/accessories') { run AccessoriesController }
19
19
  map('/characteristics') { run CharacteristicsController }
20
+ map('/identify') { run IdentifyController }
20
21
  map('/pair-setup') { run PairSetupsController }
21
22
  map('/pair-verify') { run PairVerifiesController }
22
23
  map('/pairings') { run PairingsController }
@@ -0,0 +1,27 @@
1
+ require_relative 'application_controller'
2
+
3
+ module RubyHome
4
+ module HTTP
5
+ class IdentifyController < ApplicationController
6
+ post '/' do
7
+ if accessory_info.paired?
8
+ paired_accessory
9
+ else
10
+ unpaired_accessory
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def paired_accessory
17
+ status 400
18
+ content_type 'application/hap+json'
19
+ JSON.generate({"status" => -70401})
20
+ end
21
+
22
+ def unpaired_accessory
23
+ halt 204
24
+ end
25
+ end
26
+ end
27
+ end
@@ -6,26 +6,59 @@ module RubyHome
6
6
 
7
7
  self.source = 'identifier_cache.yml'
8
8
 
9
- def self.instance
10
- @@_instance ||= persisted || create
9
+ def self.all
10
+ raw_items = read || []
11
+ raw_items.map do |raw_item|
12
+ new(**raw_item)
13
+ end
11
14
  end
12
15
 
13
16
  def self.reload
14
17
  @@_instance = nil
15
18
  end
16
19
 
17
- def initialize(accessory_id: , instance_id: , uuid: )
20
+ def self.create(**attributes)
21
+ new(**attributes).save
22
+ end
23
+
24
+ def self.find_by(**attributes)
25
+ all.find do |identifier_cache|
26
+ attributes.all? do |key, value|
27
+ identifier_cache.send(key) == value
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.where(**attributes)
33
+ all.select do |identifier_cache|
34
+ attributes.all? do |key, value|
35
+ identifier_cache.send(key) == value
36
+ end
37
+ end
38
+ end
39
+
40
+ def initialize(accessory_id:, instance_id:, service_uuid: nil, subtype: , uuid: )
18
41
  @accessory_id = accessory_id
19
42
  @instance_id = instance_id
43
+ @subtype = subtype
44
+ @service_uuid = service_uuid
20
45
  @uuid = uuid
21
46
  end
22
47
 
48
+ attr_reader :accessory_id, :instance_id, :service_uuid, :subtype, :uuid
49
+
23
50
  def persisted_attributes
24
- {
25
- accessory_id: accessory.id,
26
- instance_id: instance.instance_id,
27
- uuid: instance.uuid
28
- }
51
+ existing_items = self.class.all
52
+ existing_items << self
53
+ existing_items.map do |identifier|
54
+ {
55
+ accessory_id: identifier.accessory_id,
56
+ instance_id: identifier.instance_id,
57
+ subtype: identifier.subtype,
58
+ service_uuid: identifier.service_uuid,
59
+ uuid: identifier.uuid,
60
+ }
61
+ end
29
62
  end
30
63
  end
31
64
  end
@@ -1,3 +1,3 @@
1
1
  module RubyHome
2
- VERSION = '0.1.7'
2
+ VERSION = '0.1.8'
3
3
  end
data/rubyhome.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'dnssd', '~> 3.0'
31
31
  spec.add_dependency 'hkdf', '~> 0.3.0'
32
32
  spec.add_dependency 'nio4r', '~> 2.3', '>= 2.3.1'
33
- spec.add_dependency 'oj', '~> 3.4'
33
+ spec.add_dependency 'oj', '3.7.0'
34
34
  spec.add_dependency 'rbnacl', '~> 5.0'
35
35
  spec.add_dependency 'rbnacl-libsodium', '~> 1.0', '>= 1.0.16'
36
36
  spec.add_dependency 'ruby_home-srp', '~> 1.2'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_home
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Entwistle
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-28 00:00:00.000000000 Z
11
+ date: 2018-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -96,16 +96,16 @@ dependencies:
96
96
  name: oj
97
97
  requirement: !ruby/object:Gem::Requirement
98
98
  requirements:
99
- - - "~>"
99
+ - - '='
100
100
  - !ruby/object:Gem::Version
101
- version: '3.4'
101
+ version: 3.7.0
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
- - - "~>"
106
+ - - '='
107
107
  - !ruby/object:Gem::Version
108
- version: '3.4'
108
+ version: 3.7.0
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: rbnacl
111
111
  requirement: !ruby/object:Gem::Requirement
@@ -328,6 +328,7 @@ files:
328
328
  - lib/ruby_home/http/controllers/accessories_controller.rb
329
329
  - lib/ruby_home/http/controllers/application_controller.rb
330
330
  - lib/ruby_home/http/controllers/characteristics_controller.rb
331
+ - lib/ruby_home/http/controllers/identify_controller.rb
331
332
  - lib/ruby_home/http/controllers/pair_setups_controller.rb
332
333
  - lib/ruby_home/http/controllers/pair_verifies_controller.rb
333
334
  - lib/ruby_home/http/controllers/pairings_controller.rb
@@ -366,7 +367,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
366
367
  version: '0'
367
368
  requirements: []
368
369
  rubyforge_project:
369
- rubygems_version: 2.7.5
370
+ rubygems_version: 2.7.6
370
371
  signing_key:
371
372
  specification_version: 4
372
373
  summary: Ruby HomeKit support