ruby_home 0.1.5 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +64 -5
- data/bin/rubyhome +4 -4
- data/lib/ruby_home/accessory_info.rb +45 -30
- data/lib/ruby_home/factories/characteristic_factory.rb +27 -19
- data/lib/ruby_home/factories/default_values/float_value.rb +1 -1
- data/lib/ruby_home/factories/service_factory.rb +89 -0
- data/lib/ruby_home/factories/templates/characteristic_template.rb +6 -2
- data/lib/ruby_home/hap/accessory.rb +24 -3
- data/lib/ruby_home/hap/accessory_collection.rb +38 -0
- data/lib/ruby_home/hap/characteristic.rb +25 -19
- data/lib/ruby_home/hap/crypto/session_key.rb +31 -0
- data/lib/ruby_home/hap/ev_response.rb +64 -0
- data/lib/ruby_home/{http → hap}/hap_request.rb +12 -5
- data/lib/ruby_home/{http → hap}/hap_response.rb +7 -4
- data/lib/ruby_home/hap/server.rb +81 -0
- data/lib/ruby_home/hap/service.rb +11 -15
- data/lib/ruby_home/http/application.rb +17 -22
- data/lib/ruby_home/http/controllers/application_controller.rb +8 -4
- data/lib/ruby_home/http/controllers/characteristics_controller.rb +19 -4
- data/lib/ruby_home/http/controllers/pair_verifies_controller.rb +3 -5
- data/lib/ruby_home/http/serializers/object_serializer.rb +1 -1
- data/lib/ruby_home/http/services/socket_notifier.rb +40 -0
- data/lib/ruby_home/http/services/start_srp_service.rb +36 -34
- data/lib/ruby_home/http/services/verify_srp_service.rb +55 -53
- data/lib/ruby_home/identifier_cache.rb +22 -49
- data/lib/ruby_home/persistable.rb +36 -0
- data/lib/ruby_home/version.rb +1 -1
- data/lib/ruby_home.rb +14 -5
- data/rubyhome.gemspec +3 -3
- metadata +35 -38
- data/lib/ruby_home/factories/accessory_factory.rb +0 -73
- data/lib/ruby_home/http/hap_server.rb +0 -60
- data/lib/ruby_home/rack/handler/hap_server.rb +0 -21
- data/lib/ruby_home/yaml_record.rb +0 -440
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7f5f53b947e2cfbce79204572dd8d29b284c5434304aee7214b6a7a33d3e0a6
|
4
|
+
data.tar.gz: '086845819574df45f838c8ff67979f55f01763b6fe44114af76fd3479f8f1b83'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 419df1283d71010c0a4e8e89f60f01fa752ea9366f6fe59a1ca3a2e202a9ab72361ed4dff5389ecdfb8b084feb40cd0d5c62fcbf16b90b65bfcd5096c7a3a4ab
|
7
|
+
data.tar.gz: 3644d107b711edadd639f881614ebd85cb8e164cbeebf5db30e1748303983b52bf056371e952a6c9c0e39aa2a7744351549ddaf2eef4c655428dabdf0c9fe2eb
|
data/README.md
CHANGED
@@ -21,18 +21,18 @@ Or install it yourself as:
|
|
21
21
|
|
22
22
|
$ gem install ruby_home
|
23
23
|
|
24
|
-
## Usage
|
24
|
+
## Basic Usage
|
25
25
|
|
26
26
|
Create a fan with an on/off switch.
|
27
27
|
|
28
28
|
```ruby
|
29
29
|
require 'ruby_home'
|
30
30
|
|
31
|
-
accessory_information = RubyHome::
|
32
|
-
fan = RubyHome::
|
31
|
+
accessory_information = RubyHome::ServiceFactory.create(:accessory_information)
|
32
|
+
fan = RubyHome::ServiceFactory.create(:fan)
|
33
33
|
|
34
|
-
fan.characteristic(:on).
|
35
|
-
if
|
34
|
+
fan.characteristic(:on).after_update do |characteristic|
|
35
|
+
if characteristic.value == 1
|
36
36
|
puts "Fan switched on"
|
37
37
|
else
|
38
38
|
puts "Fan switched off"
|
@@ -42,6 +42,65 @@ end
|
|
42
42
|
RubyHome.run
|
43
43
|
```
|
44
44
|
|
45
|
+
Create a garage door opener.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require 'ruby_home'
|
49
|
+
|
50
|
+
accessory_information = RubyHome::ServiceFactory.create(:accessory_information)
|
51
|
+
door = RubyHome::ServiceFactory.create(:garage_door_opener)
|
52
|
+
|
53
|
+
door.characteristic(:target_door_state).after_update do |characteristic|
|
54
|
+
if characteristic.value == 0 # open
|
55
|
+
sleep 1
|
56
|
+
door.characteristic(:current_door_state).value = 0
|
57
|
+
elsif characteristic.value == 1 #closed
|
58
|
+
sleep 1
|
59
|
+
door.characteristic(:current_door_state).value = 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
RubyHome.run
|
64
|
+
```
|
65
|
+
|
66
|
+
## Customization
|
67
|
+
|
68
|
+
RubyHome ties to provide sane defaults for all services. Customization of any of the options is possible.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
require 'ruby_home'
|
72
|
+
|
73
|
+
accessory_information = RubyHome::ServiceFactory.create(:accessory_information,
|
74
|
+
firmware_revision: '4.3.18421',
|
75
|
+
manufacturer: 'Fake Company',
|
76
|
+
model: 'BSB001',
|
77
|
+
name: 'Kickass fan bridge',
|
78
|
+
serial_number: 'AB1-UK-A123456'
|
79
|
+
)
|
80
|
+
|
81
|
+
fan = RubyHome::ServiceFactory.create(:fan,
|
82
|
+
on: false,
|
83
|
+
rotation_speed: 50,
|
84
|
+
rotation_direction: 1,
|
85
|
+
firmware_revision: '105.0.21169',
|
86
|
+
manufacturer: 'Fake Company',
|
87
|
+
model: 'LWB006',
|
88
|
+
name: 'Kickass fan',
|
89
|
+
serial_number: '123-UK-A12345'
|
90
|
+
)
|
91
|
+
|
92
|
+
fan.characteristic(:on).after_update do |characteristic|
|
93
|
+
if characteristic.value == 1
|
94
|
+
puts "Fan switched on"
|
95
|
+
else
|
96
|
+
puts "Fan switched off"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
RubyHome.run
|
101
|
+
```
|
102
|
+
|
103
|
+
|
45
104
|
## Development
|
46
105
|
|
47
106
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/bin/rubyhome
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
require_relative '../lib/ruby_home'
|
4
4
|
|
5
|
-
accessory_information = RubyHome::
|
6
|
-
fan = RubyHome::
|
5
|
+
accessory_information = RubyHome::ServiceFactory.create(:accessory_information)
|
6
|
+
fan = RubyHome::ServiceFactory.create(:fan)
|
7
7
|
|
8
|
-
fan.characteristic(:on).
|
9
|
-
if
|
8
|
+
fan.characteristic(:on).after_update do |characteristic|
|
9
|
+
if characteristic.value == 1
|
10
10
|
puts "Fan switched on"
|
11
11
|
else
|
12
12
|
puts "Fan switched off"
|
@@ -1,24 +1,46 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative 'yaml_record'
|
1
|
+
require_relative 'persistable'
|
3
2
|
|
4
3
|
module RubyHome
|
5
|
-
class AccessoryInfo
|
4
|
+
class AccessoryInfo
|
5
|
+
include Persistable
|
6
|
+
|
7
|
+
self.source = 'accessory_info.yml'
|
8
|
+
|
9
|
+
def self.instance
|
10
|
+
@@_instance ||= persisted || create
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.reload
|
14
|
+
@@_instance = nil
|
15
|
+
end
|
16
|
+
|
6
17
|
USERNAME = -'Pair-Setup'
|
7
18
|
|
8
|
-
|
9
|
-
|
19
|
+
def initialize(device_id: nil, paired_clients: [], password: nil, signature_key: nil)
|
20
|
+
@device_id = device_id
|
21
|
+
@paired_clients = paired_clients
|
22
|
+
@password = password
|
23
|
+
@signature_key = signature_key
|
24
|
+
end
|
10
25
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
set_callback :before_create, :set_signature_key
|
26
|
+
def username
|
27
|
+
USERNAME
|
28
|
+
end
|
15
29
|
|
16
|
-
def
|
17
|
-
|
30
|
+
def password
|
31
|
+
@password ||= Password.generate
|
32
|
+
end
|
33
|
+
|
34
|
+
def device_id
|
35
|
+
@device_id ||= DeviceID.generate
|
36
|
+
end
|
37
|
+
|
38
|
+
def paired_clients
|
39
|
+
@paired_clients ||= []
|
18
40
|
end
|
19
41
|
|
20
42
|
def add_paired_client(admin: false, identifier: , public_key: )
|
21
|
-
paired_clients << { admin: admin, identifier: identifier, public_key: public_key }
|
43
|
+
@paired_clients << { admin: admin, identifier: identifier, public_key: public_key }
|
22
44
|
save
|
23
45
|
end
|
24
46
|
|
@@ -35,26 +57,19 @@ module RubyHome
|
|
35
57
|
@signing_key ||= RbNaCl::Signatures::Ed25519::SigningKey.new([signature_key].pack('H*'))
|
36
58
|
end
|
37
59
|
|
38
|
-
def username
|
39
|
-
USERNAME
|
40
|
-
end
|
41
|
-
|
42
60
|
private
|
43
61
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def set_paired_clients
|
49
|
-
self.paired_clients = []
|
50
|
-
end
|
62
|
+
def signature_key
|
63
|
+
@signature_key ||= RbNaCl::Signatures::Ed25519::SigningKey.generate.to_bytes.unpack1('H*')
|
64
|
+
end
|
51
65
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
66
|
+
def persisted_attributes
|
67
|
+
{
|
68
|
+
device_id: device_id,
|
69
|
+
paired_clients: paired_clients,
|
70
|
+
password: password,
|
71
|
+
signature_key: signature_key
|
72
|
+
}
|
73
|
+
end
|
59
74
|
end
|
60
75
|
end
|
@@ -4,37 +4,45 @@ module RubyHome
|
|
4
4
|
identify: nil,
|
5
5
|
}.freeze
|
6
6
|
|
7
|
-
def self.create(characteristic_name,
|
8
|
-
new(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@options = options
|
7
|
+
def self.create(characteristic_name, service: , value: nil)
|
8
|
+
new(
|
9
|
+
characteristic_name: characteristic_name,
|
10
|
+
service: service,
|
11
|
+
value: value
|
12
|
+
).create
|
14
13
|
end
|
15
14
|
|
16
15
|
def create
|
17
|
-
|
16
|
+
service.characteristics << new_characteristic
|
18
17
|
|
19
|
-
|
20
|
-
characteristic
|
18
|
+
new_characteristic
|
21
19
|
end
|
22
20
|
|
23
21
|
private
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@
|
23
|
+
def initialize(characteristic_name:, service:, value:)
|
24
|
+
@characteristic_name = characteristic_name.to_sym
|
25
|
+
@service = service
|
26
|
+
@value = value || default_value
|
29
27
|
end
|
30
28
|
|
31
|
-
|
32
|
-
|
29
|
+
attr_reader :service, :characteristic_name, :value
|
30
|
+
|
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
|
+
)
|
33
42
|
end
|
34
43
|
|
35
|
-
def
|
36
|
-
|
37
|
-
options.merge(template.to_hash)
|
44
|
+
def template
|
45
|
+
@template ||= CharacteristicTemplate.find_by(name: characteristic_name)
|
38
46
|
end
|
39
47
|
|
40
48
|
def default_value
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module RubyHome
|
2
|
+
class ServiceFactory
|
3
|
+
def self.create(service_name, accessory: Accessory.new, **options)
|
4
|
+
new(
|
5
|
+
service_name: service_name,
|
6
|
+
accessory: accessory,
|
7
|
+
**options
|
8
|
+
).create
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
accessory.services << new_service
|
13
|
+
|
14
|
+
create_accessory_information
|
15
|
+
create_required_characteristics
|
16
|
+
create_optional_characteristics
|
17
|
+
|
18
|
+
new_service
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
ACCESSORY_INFORMATION_OPTIONS = [
|
23
|
+
:firmware_revision,
|
24
|
+
:manufacturer,
|
25
|
+
:model,
|
26
|
+
:name,
|
27
|
+
:serial_number
|
28
|
+
].freeze
|
29
|
+
|
30
|
+
def initialize(service_name:, accessory:, **options)
|
31
|
+
@service_name = service_name.to_sym
|
32
|
+
@accessory = accessory
|
33
|
+
@options = options
|
34
|
+
end
|
35
|
+
|
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
|
46
|
+
|
47
|
+
def create_required_characteristics
|
48
|
+
template.required_characteristics.each do |characteristic_template|
|
49
|
+
characteristic_name = characteristic_template.name
|
50
|
+
CharacteristicFactory.create(
|
51
|
+
characteristic_template.name,
|
52
|
+
service: new_service,
|
53
|
+
value: options[characteristic_name]
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_optional_characteristics
|
59
|
+
optional_characteristic_templates.each do |characteristic_template|
|
60
|
+
characteristic_name = characteristic_template.name
|
61
|
+
CharacteristicFactory.create(
|
62
|
+
characteristic_name,
|
63
|
+
service: new_service,
|
64
|
+
value: options[characteristic_name]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def optional_characteristic_templates
|
70
|
+
template.optional_characteristics.select do |characteristic_template|
|
71
|
+
options.keys.include?(characteristic_template.name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
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
|
83
|
+
end
|
84
|
+
|
85
|
+
def template
|
86
|
+
@template ||= ServiceTemplate.find_by(name: service_name)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -15,7 +15,7 @@ module RubyHome
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def initialize(name:, description:, uuid:, format:, unit:, permissions:, properties:, constraints:)
|
18
|
+
def initialize(name:, description:, uuid:, format:, unit:, permissions:, properties:, constraints: )
|
19
19
|
@name = name
|
20
20
|
@description = description
|
21
21
|
@uuid = uuid
|
@@ -26,7 +26,11 @@ module RubyHome
|
|
26
26
|
@constraints = constraints
|
27
27
|
end
|
28
28
|
|
29
|
-
attr_reader :name, :description, :uuid, :format, :unit, :permissions, :properties
|
29
|
+
attr_reader :name, :description, :uuid, :format, :unit, :permissions, :properties
|
30
|
+
|
31
|
+
def constraints
|
32
|
+
@constraints || {}
|
33
|
+
end
|
30
34
|
|
31
35
|
def to_hash
|
32
36
|
{
|
@@ -1,11 +1,22 @@
|
|
1
1
|
module RubyHome
|
2
2
|
class Accessory
|
3
|
+
@@all = AccessoryCollection.new
|
4
|
+
|
5
|
+
def self.all
|
6
|
+
@@all
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.reset
|
10
|
+
@@all = AccessoryCollection.new
|
11
|
+
end
|
12
|
+
|
3
13
|
def initialize
|
4
14
|
@services = []
|
15
|
+
@id = next_available_accessory_id
|
16
|
+
@@all << self
|
5
17
|
end
|
6
18
|
|
7
|
-
|
8
|
-
attr_reader :services
|
19
|
+
attr_reader :services, :id
|
9
20
|
|
10
21
|
def characteristics
|
11
22
|
services.flat_map(&:characteristics)
|
@@ -15,12 +26,22 @@ module RubyHome
|
|
15
26
|
(largest_instance_id || 0) + 1
|
16
27
|
end
|
17
28
|
|
29
|
+
private
|
30
|
+
|
18
31
|
def instance_ids
|
19
|
-
|
32
|
+
instances.map(&:instance_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
def instances
|
36
|
+
services + characteristics
|
20
37
|
end
|
21
38
|
|
22
39
|
def largest_instance_id
|
23
40
|
instance_ids.max
|
24
41
|
end
|
42
|
+
|
43
|
+
def next_available_accessory_id
|
44
|
+
self.class.all.count + 1
|
45
|
+
end
|
25
46
|
end
|
26
47
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RubyHome
|
2
|
+
class AccessoryCollection
|
3
|
+
include Enumerable
|
4
|
+
attr_accessor :accessories
|
5
|
+
|
6
|
+
def initialize(*accessories)
|
7
|
+
@accessories = accessories
|
8
|
+
end
|
9
|
+
|
10
|
+
def each
|
11
|
+
@accessories.map do |accessory|
|
12
|
+
yield accessory
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def <<(accessory)
|
17
|
+
@accessories << accessory
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_characteristic(attributes)
|
21
|
+
characteristics.find do |characteristic|
|
22
|
+
attributes.all? do |key, value|
|
23
|
+
characteristic.send(key) == value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def services
|
31
|
+
accessories.flat_map(&:services)
|
32
|
+
end
|
33
|
+
|
34
|
+
def characteristics
|
35
|
+
services.flat_map(&:characteristics)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -2,6 +2,18 @@ module RubyHome
|
|
2
2
|
class Characteristic
|
3
3
|
include Wisper::Publisher
|
4
4
|
|
5
|
+
public :local_registrations
|
6
|
+
|
7
|
+
def unsubscribe(*listeners)
|
8
|
+
local_registrations.delete_if do |registration|
|
9
|
+
listeners.include?(registration.listener)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_update(&block)
|
14
|
+
on(:after_update, &block)
|
15
|
+
end
|
16
|
+
|
5
17
|
PROPERTIES = {
|
6
18
|
'cnotify' => 'ev',
|
7
19
|
'read' => 'pr',
|
@@ -18,10 +30,20 @@ module RubyHome
|
|
18
30
|
@properties = properties
|
19
31
|
@service = service
|
20
32
|
@value = value
|
33
|
+
@instance_id = accessory.next_available_instance_id
|
21
34
|
end
|
22
35
|
|
23
|
-
attr_reader
|
24
|
-
|
36
|
+
attr_reader(
|
37
|
+
:service,
|
38
|
+
:value,
|
39
|
+
:uuid,
|
40
|
+
:name,
|
41
|
+
:description,
|
42
|
+
:format,
|
43
|
+
:unit,
|
44
|
+
:properties,
|
45
|
+
:instance_id
|
46
|
+
)
|
25
47
|
|
26
48
|
def accessory
|
27
49
|
service.accessory
|
@@ -37,24 +59,8 @@ module RubyHome
|
|
37
59
|
|
38
60
|
def value=(new_value)
|
39
61
|
return if name == :identify
|
40
|
-
|
41
62
|
@value = new_value
|
42
|
-
broadcast(:
|
43
|
-
end
|
44
|
-
|
45
|
-
def inspect
|
46
|
-
{
|
47
|
-
name: name,
|
48
|
-
value: value,
|
49
|
-
accessory_id: accessory_id,
|
50
|
-
service_iid: service_iid,
|
51
|
-
instance_id: instance_id
|
52
|
-
}
|
53
|
-
end
|
54
|
-
|
55
|
-
def save
|
56
|
-
IdentifierCache.add_accessory(accessory)
|
57
|
-
IdentifierCache.add_characteristic(self)
|
63
|
+
broadcast(:after_update, self)
|
58
64
|
end
|
59
65
|
end
|
60
66
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RubyHome
|
2
|
+
module HAP
|
3
|
+
module Crypto
|
4
|
+
class SessionKey
|
5
|
+
def initialize(shared_secret)
|
6
|
+
@shared_secret = shared_secret
|
7
|
+
end
|
8
|
+
|
9
|
+
def controller_to_accessory_key
|
10
|
+
@controller_to_accessory_key ||= generate_shared_secret_key(WRITE)
|
11
|
+
end
|
12
|
+
|
13
|
+
def accessory_to_controller_key
|
14
|
+
@accessory_to_controller_key ||= generate_shared_secret_key(READ)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
SALT = -'Control-Salt'
|
20
|
+
READ = -'Control-Read-Encryption-Key'
|
21
|
+
WRITE = -'Control-Write-Encryption-Key'
|
22
|
+
|
23
|
+
attr_reader :shared_secret
|
24
|
+
|
25
|
+
def generate_shared_secret_key(info)
|
26
|
+
Crypto::HKDF.new(info: info, salt: SALT).encrypt(shared_secret)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module RubyHome
|
2
|
+
module HAP
|
3
|
+
class EVResponse
|
4
|
+
def initialize(socket, body)
|
5
|
+
@socket = socket
|
6
|
+
@body = body
|
7
|
+
cache[:accessory_to_controller_count] ||= 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def send_response
|
11
|
+
response = ''
|
12
|
+
|
13
|
+
send_header(response)
|
14
|
+
send_body(response)
|
15
|
+
|
16
|
+
encrypted_response = encrypter.encrypt(response).join
|
17
|
+
cache[:accessory_to_controller_count] = encrypter.count
|
18
|
+
|
19
|
+
socket << encrypted_response
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
CRLF = -"\x0d\x0a"
|
25
|
+
STATUS_LINE = -'EVENT/1.0 200 OK'
|
26
|
+
CONTENT_TYPE_LINE = -'Content-Type: application/hap+json'
|
27
|
+
|
28
|
+
def send_header(socket)
|
29
|
+
socket << STATUS_LINE + CRLF
|
30
|
+
socket << CONTENT_TYPE_LINE + CRLF
|
31
|
+
socket << content_length_line + CRLF
|
32
|
+
socket << CRLF
|
33
|
+
end
|
34
|
+
|
35
|
+
def send_body(socket)
|
36
|
+
socket << body
|
37
|
+
end
|
38
|
+
|
39
|
+
def content_length_line
|
40
|
+
"Content-Length: #{body.length}"
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :body, :socket
|
44
|
+
|
45
|
+
def encrypter
|
46
|
+
@_encrypter ||= RubyHome::HAP::HTTPEncryption.new(encryption_key, encrypter_params)
|
47
|
+
end
|
48
|
+
|
49
|
+
def encrypter_params
|
50
|
+
{
|
51
|
+
count: cache[:accessory_to_controller_count]
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def encryption_key
|
56
|
+
cache[:accessory_to_controller_key]
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache
|
60
|
+
RubyHome.socket_store[socket]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
module RubyHome
|
2
|
-
module
|
2
|
+
module HAP
|
3
3
|
class HAPRequest < WEBrick::HTTPRequest
|
4
|
-
def initialize(
|
4
|
+
def initialize(config, sock)
|
5
|
+
@sock = sock
|
5
6
|
cache[:controller_to_accessory_count] ||= 0
|
6
7
|
|
7
|
-
super(
|
8
|
+
super(config)
|
8
9
|
end
|
9
10
|
|
10
|
-
def parse(socket
|
11
|
+
def parse(socket)
|
11
12
|
if decryption_time?
|
12
13
|
request_line = socket.read_nonblock(@buffer_size)
|
13
14
|
|
@@ -24,8 +25,14 @@ module RubyHome
|
|
24
25
|
cache[:controller_to_accessory_count] >= 1
|
25
26
|
end
|
26
27
|
|
28
|
+
def meta_vars
|
29
|
+
super.merge({"REQUEST_SOCKET" => sock})
|
30
|
+
end
|
31
|
+
|
27
32
|
private
|
28
33
|
|
34
|
+
attr_reader :sock
|
35
|
+
|
29
36
|
def decrypter
|
30
37
|
@_decrypter ||= RubyHome::HAP::HTTPDecryption.new(decryption_key, decrypter_params)
|
31
38
|
end
|
@@ -45,7 +52,7 @@ module RubyHome
|
|
45
52
|
end
|
46
53
|
|
47
54
|
def cache
|
48
|
-
|
55
|
+
RubyHome.socket_store[sock]
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|