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 +4 -4
- data/README.md +1 -1
- data/lib/ruby_home/factories/characteristic_factory.rb +45 -17
- data/lib/ruby_home/factories/service_factory.rb +63 -28
- data/lib/ruby_home/hap/accessory.rb +13 -1
- data/lib/ruby_home/hap/characteristic.rb +8 -6
- data/lib/ruby_home/hap/hap_response.rb +2 -2
- data/lib/ruby_home/hap/service.rb +10 -1
- data/lib/ruby_home/http/application.rb +1 -0
- data/lib/ruby_home/http/controllers/identify_controller.rb +27 -0
- data/lib/ruby_home/identifier_cache.rb +41 -8
- data/lib/ruby_home/version.rb +1 -1
- data/rubyhome.gemspec +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dff58e7d6769b9869dd1c7368ced5443698c27197ca89c4f85eafe9320fb1f62
|
4
|
+
data.tar.gz: 9ee496a8ebb0a2c7d1693501e5bef6cb14d99cc92d6d70aa34c04ca81947ff33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
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:
|
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:
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
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
|
49
|
-
|
50
|
-
end
|
49
|
+
def instance_id=(new_id)
|
50
|
+
raise DuplicateCharacteristicError if accessory.contains_instance_id?(new_id)
|
51
51
|
|
52
|
-
|
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 =
|
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.
|
10
|
-
|
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
|
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
|
-
|
26
|
-
|
27
|
-
|
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
|
data/lib/ruby_home/version.rb
CHANGED
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', '
|
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.
|
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-
|
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:
|
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:
|
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.
|
370
|
+
rubygems_version: 2.7.6
|
370
371
|
signing_key:
|
371
372
|
specification_version: 4
|
372
373
|
summary: Ruby HomeKit support
|