ruby_home 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
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