ruby_home 0.2.5 → 0.2.6
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/.github/workflows/tests.yml +4 -4
- data/.gitignore +2 -2
- data/.rubocop.yml +2 -9
- data/.standard.yml +3 -0
- data/CHANGELOG.md +6 -1
- data/Gemfile +2 -2
- data/Rakefile +4 -4
- data/examples/air_purifier.rb +7 -8
- data/examples/air_quality_sensor.rb +8 -9
- data/examples/battery_service.rb +2 -3
- data/examples/carbon_dioxide_sensor.rb +2 -3
- data/examples/carbon_monoxide_sensor.rb +3 -4
- data/examples/contact_sensor.rb +2 -3
- data/examples/door.rb +5 -6
- data/examples/fan.rb +2 -3
- data/examples/fan_v2.rb +2 -3
- data/examples/garage_door_opener.rb +9 -10
- data/examples/heater_cooler.rb +9 -10
- data/examples/humidifier_dehumidifier.rb +9 -10
- data/examples/humidity_sensor.rb +2 -3
- data/examples/leak_sensor.rb +2 -3
- data/examples/light_sensor.rb +2 -3
- data/examples/lightbulb.rb +2 -3
- data/examples/lock_mechanism.rb +6 -7
- data/examples/motion_sensor.rb +2 -3
- data/examples/occupancy_sensor.rb +2 -3
- data/examples/outlet.rb +2 -3
- data/examples/security_system.rb +12 -13
- data/examples/smoke_sensor.rb +2 -3
- data/examples/switch.rb +2 -3
- data/examples/television.rb +8 -13
- data/examples/temperature_sensor.rb +2 -3
- data/examples/thermostat.rb +10 -11
- data/examples/window.rb +5 -6
- data/examples/window_covering.rb +5 -6
- data/lib/ruby_home.rb +21 -21
- data/lib/ruby_home/accessory.rb +1 -1
- data/lib/ruby_home/accessory_collection.rb +6 -6
- data/lib/ruby_home/accessory_info.rb +18 -18
- data/lib/ruby_home/characteristic.rb +8 -8
- data/lib/ruby_home/characteristic_collection.rb +13 -13
- data/lib/ruby_home/config/characteristics.yml +30 -635
- data/lib/ruby_home/config/manual_characteristics.yml +278 -0
- data/lib/ruby_home/config/manual_services.yml +45 -0
- data/lib/ruby_home/config/services.yml +329 -370
- data/lib/ruby_home/configuration.rb +4 -4
- data/lib/ruby_home/device_id.rb +3 -3
- data/lib/ruby_home/dns/service.rb +4 -6
- data/lib/ruby_home/dns/text_record.rb +9 -9
- data/lib/ruby_home/errors.rb +1 -0
- data/lib/ruby_home/factories/characteristic_factory.rb +37 -37
- data/lib/ruby_home/factories/service_factory.rb +72 -72
- data/lib/ruby_home/factories/templates/characteristic_template.rb +5 -5
- data/lib/ruby_home/factories/templates/service_template.rb +11 -10
- data/lib/ruby_home/hap/crypto/chacha20poly1305.rb +2 -2
- data/lib/ruby_home/hap/crypto/hkdf.rb +4 -4
- data/lib/ruby_home/hap/crypto/session_key.rb +7 -7
- data/lib/ruby_home/hap/decrypter.rb +8 -8
- data/lib/ruby_home/hap/encrypter.rb +5 -6
- data/lib/ruby_home/hap/ev_response.rb +17 -17
- data/lib/ruby_home/hap/hap_request.rb +1 -1
- data/lib/ruby_home/hap/server.rb +4 -4
- data/lib/ruby_home/hap/server_handler.rb +8 -10
- data/lib/ruby_home/hap/session.rb +22 -22
- data/lib/ruby_home/hap/values/base_value.rb +3 -3
- data/lib/ruby_home/hap/values/bool_value.rb +4 -4
- data/lib/ruby_home/hap/values/float_value.rb +4 -4
- data/lib/ruby_home/hap/values/identify_value.rb +1 -1
- data/lib/ruby_home/hap/values/int32_value.rb +4 -4
- data/lib/ruby_home/hap/values/null_value.rb +1 -1
- data/lib/ruby_home/hap/values/string_value.rb +11 -11
- data/lib/ruby_home/hap/values/uint32_value.rb +5 -5
- data/lib/ruby_home/hap/values/uint8_value.rb +14 -14
- data/lib/ruby_home/hex_helper.rb +3 -3
- data/lib/ruby_home/http/application.rb +7 -7
- data/lib/ruby_home/http/controllers/accessories_controller.rb +3 -3
- data/lib/ruby_home/http/controllers/application_controller.rb +6 -6
- data/lib/ruby_home/http/controllers/characteristics_controller.rb +29 -29
- data/lib/ruby_home/http/controllers/identify_controller.rb +10 -10
- data/lib/ruby_home/http/controllers/pair_setups_controller.rb +18 -18
- data/lib/ruby_home/http/controllers/pair_verifies_controller.rb +9 -9
- data/lib/ruby_home/http/controllers/pairings_controller.rb +5 -5
- data/lib/ruby_home/http/serializers/accessory_serializer.rb +5 -5
- data/lib/ruby_home/http/serializers/characteristic_serializer.rb +24 -24
- data/lib/ruby_home/http/serializers/characteristic_value_serializer.rb +5 -5
- data/lib/ruby_home/http/serializers/object_serializer.rb +1 -1
- data/lib/ruby_home/http/serializers/service_serializer.rb +9 -9
- data/lib/ruby_home/http/serializers/uuid_helper.rb +2 -2
- data/lib/ruby_home/http/services/session_notifier.rb +8 -8
- data/lib/ruby_home/http/services/start_srp_service.rb +3 -3
- data/lib/ruby_home/http/services/verify_finish_service.rb +22 -22
- data/lib/ruby_home/http/services/verify_srp_service.rb +22 -22
- data/lib/ruby_home/identifier_cache.rb +4 -5
- data/lib/ruby_home/password.rb +14 -14
- data/lib/ruby_home/persistable.rb +22 -7
- data/lib/ruby_home/service.rb +3 -3
- data/lib/ruby_home/service_collection.rb +1 -1
- data/lib/ruby_home/version.rb +1 -1
- data/rubyhome.gemspec +31 -31
- data/sbin/characteristic_generator.rb +22 -24
- data/sbin/service_generator.rb +36 -27
- metadata +25 -8
@@ -6,11 +6,11 @@ module RubyHome
|
|
6
6
|
@key = key
|
7
7
|
end
|
8
8
|
|
9
|
-
def encrypt(nonce, message, additional_data=nil)
|
9
|
+
def encrypt(nonce, message, additional_data = nil)
|
10
10
|
chacha20poly1305ietf.encrypt(nonce, message, additional_data)
|
11
11
|
end
|
12
12
|
|
13
|
-
def decrypt(nonce, ciphertext, additional_data=nil)
|
13
|
+
def decrypt(nonce, ciphertext, additional_data = nil)
|
14
14
|
chacha20poly1305ietf.decrypt(nonce, ciphertext, additional_data)
|
15
15
|
end
|
16
16
|
|
@@ -4,19 +4,19 @@ module RubyHome
|
|
4
4
|
module HAP
|
5
5
|
module Crypto
|
6
6
|
class HKDF
|
7
|
-
def initialize(salt:, info:
|
7
|
+
def initialize(salt:, info:)
|
8
8
|
@salt = salt
|
9
9
|
@info = info
|
10
10
|
end
|
11
11
|
|
12
12
|
def encrypt(source)
|
13
13
|
byte_string = convert_string_to_byte_string(source)
|
14
|
-
GEM_HKDF.new(byte_string, hkdf_opts).
|
14
|
+
GEM_HKDF.new(byte_string, hkdf_opts).read(BYTE_LENGTH)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
ALGORITHM =
|
19
|
+
ALGORITHM = "SHA512"
|
20
20
|
BYTE_LENGTH = 32
|
21
21
|
|
22
22
|
attr_reader :info, :salt, :source
|
@@ -33,7 +33,7 @@ module RubyHome
|
|
33
33
|
if string.encoding == Encoding::ASCII_8BIT
|
34
34
|
string
|
35
35
|
else
|
36
|
-
[string].pack(
|
36
|
+
[string].pack("H*")
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -16,15 +16,15 @@ module RubyHome
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
SALT = -"Control-Salt"
|
20
|
+
READ = -"Control-Read-Encryption-Key"
|
21
|
+
WRITE = -"Control-Write-Encryption-Key"
|
22
22
|
|
23
|
-
|
23
|
+
attr_reader :shared_secret
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
def generate_shared_secret_key(info)
|
26
|
+
Crypto::HKDF.new(info: info, salt: SALT).encrypt(shared_secret)
|
27
|
+
end
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module RubyHome
|
2
2
|
module HAP
|
3
3
|
class Decrypter
|
4
|
-
AAD_LENGTH_BYTES = 2
|
5
|
-
AUTHENTICATE_TAG_LENGTH_BYTES = 16
|
6
|
-
NONCE_32_BIT_FIX_COMMENT_PART = [0].pack(
|
4
|
+
AAD_LENGTH_BYTES = 2
|
5
|
+
AUTHENTICATE_TAG_LENGTH_BYTES = 16
|
6
|
+
NONCE_32_BIT_FIX_COMMENT_PART = [0].pack("L").freeze
|
7
7
|
|
8
8
|
def initialize(key, count: 0)
|
9
9
|
@key = key
|
@@ -15,14 +15,14 @@ module RubyHome
|
|
15
15
|
read_pointer = 0
|
16
16
|
|
17
17
|
while read_pointer < data.length
|
18
|
-
little_endian_length_of_encrypted_data = data[read_pointer...read_pointer+AAD_LENGTH_BYTES]
|
19
|
-
length_of_encrypted_data = little_endian_length_of_encrypted_data.unpack1(
|
18
|
+
little_endian_length_of_encrypted_data = data[read_pointer...read_pointer + AAD_LENGTH_BYTES]
|
19
|
+
length_of_encrypted_data = little_endian_length_of_encrypted_data.unpack1("v")
|
20
20
|
read_pointer += AAD_LENGTH_BYTES
|
21
21
|
|
22
|
-
message = data[read_pointer...read_pointer+length_of_encrypted_data]
|
22
|
+
message = data[read_pointer...read_pointer + length_of_encrypted_data]
|
23
23
|
read_pointer += length_of_encrypted_data
|
24
24
|
|
25
|
-
auth_tag = data[read_pointer...read_pointer+AUTHENTICATE_TAG_LENGTH_BYTES]
|
25
|
+
auth_tag = data[read_pointer...read_pointer + AUTHENTICATE_TAG_LENGTH_BYTES]
|
26
26
|
read_pointer += AUTHENTICATE_TAG_LENGTH_BYTES
|
27
27
|
|
28
28
|
ciphertext = message + auth_tag
|
@@ -46,7 +46,7 @@ module RubyHome
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def nonce
|
49
|
-
NONCE_32_BIT_FIX_COMMENT_PART + [count].pack(
|
49
|
+
NONCE_32_BIT_FIX_COMMENT_PART + [count].pack("Q")
|
50
50
|
end
|
51
51
|
|
52
52
|
def chacha20poly1305ietf
|
@@ -2,8 +2,8 @@ module RubyHome
|
|
2
2
|
module HAP
|
3
3
|
class Session
|
4
4
|
class Encrypter
|
5
|
-
MAX_FRAME_LENGTH = 1024
|
6
|
-
NONCE_32_BIT_FIX_COMMENT_PART = [0].pack(
|
5
|
+
MAX_FRAME_LENGTH = 1024
|
6
|
+
NONCE_32_BIT_FIX_COMMENT_PART = [0].pack("L").freeze
|
7
7
|
|
8
8
|
def initialize(key, count: 0)
|
9
9
|
@key = key
|
@@ -17,9 +17,9 @@ module RubyHome
|
|
17
17
|
while read_pointer < data.length
|
18
18
|
encrypted_frame = ""
|
19
19
|
|
20
|
-
frame = data[read_pointer...read_pointer+MAX_FRAME_LENGTH]
|
20
|
+
frame = data[read_pointer...read_pointer + MAX_FRAME_LENGTH]
|
21
21
|
length_of_encrypted_data = frame.length
|
22
|
-
little_endian_length_of_encrypted_data = [length_of_encrypted_data].pack(
|
22
|
+
little_endian_length_of_encrypted_data = [length_of_encrypted_data].pack("v")
|
23
23
|
|
24
24
|
encrypted_frame += little_endian_length_of_encrypted_data
|
25
25
|
encrypted_frame += chacha20poly1305ietf.encrypt(nonce, frame, little_endian_length_of_encrypted_data)
|
@@ -43,7 +43,7 @@ module RubyHome
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def nonce
|
46
|
-
NONCE_32_BIT_FIX_COMMENT_PART + [count].pack(
|
46
|
+
NONCE_32_BIT_FIX_COMMENT_PART + [count].pack("Q")
|
47
47
|
end
|
48
48
|
|
49
49
|
def chacha20poly1305ietf
|
@@ -53,4 +53,3 @@ module RubyHome
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
@@ -13,28 +13,28 @@ module RubyHome
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
|
16
|
+
attr_reader :body, :session
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
CRLF = -"\x0d\x0a"
|
19
|
+
STATUS_LINE = -"EVENT/1.0 200 OK"
|
20
|
+
CONTENT_TYPE_LINE = -"Content-Type: application/hap+json"
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
def content_length_line
|
23
|
+
"Content-Length: #{body.length}"
|
24
|
+
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
def send_header(io)
|
27
|
+
data = STATUS_LINE + CRLF
|
28
|
+
data << CONTENT_TYPE_LINE + CRLF
|
29
|
+
data << content_length_line + CRLF
|
30
|
+
data << CRLF
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
io.write(data)
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
def send_body(io)
|
36
|
+
io.write(body)
|
37
|
+
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/ruby_home/hap/server.rb
CHANGED
@@ -4,11 +4,11 @@ module RubyHome
|
|
4
4
|
def run(sock)
|
5
5
|
session = Session.new(sock)
|
6
6
|
|
7
|
-
|
7
|
+
loop do
|
8
8
|
req = HAPRequest.new(@config, session: session)
|
9
9
|
res = HAPResponse.new(@config)
|
10
10
|
begin
|
11
|
-
|
11
|
+
loop do
|
12
12
|
break if sock.to_io.wait_readable(0.5)
|
13
13
|
break if @status != :Running
|
14
14
|
end
|
@@ -30,13 +30,13 @@ module RubyHome
|
|
30
30
|
res.set_error(ex)
|
31
31
|
rescue ::WEBrick::HTTPStatus::Status => ex
|
32
32
|
res.status = ex.code
|
33
|
-
rescue
|
33
|
+
rescue => ex
|
34
34
|
@logger.error(ex)
|
35
35
|
res.set_error(ex, true)
|
36
36
|
ensure
|
37
37
|
if req.request_line
|
38
38
|
if req.keep_alive? && res.keep_alive?
|
39
|
-
req.fixup
|
39
|
+
req.fixup
|
40
40
|
end
|
41
41
|
res.send_response(session)
|
42
42
|
access_log(@config, req, res)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RubyHome
|
2
2
|
module HAP
|
3
3
|
class RackHandler < ::Rack::Handler::WEBrick
|
4
|
-
def self.run(app, options={})
|
4
|
+
def self.run(app, options = {})
|
5
5
|
@server = Server.new(options)
|
6
6
|
@server.mount "/", RackHandler, app
|
7
7
|
yield @server if block_given?
|
@@ -10,7 +10,7 @@ module RubyHome
|
|
10
10
|
end
|
11
11
|
|
12
12
|
class ServerHandler
|
13
|
-
def initialize(configuration:
|
13
|
+
def initialize(configuration:)
|
14
14
|
@configuration = configuration
|
15
15
|
end
|
16
16
|
|
@@ -33,19 +33,19 @@ module RubyHome
|
|
33
33
|
{
|
34
34
|
Port: port,
|
35
35
|
Host: bind_address,
|
36
|
-
ServerSoftware:
|
36
|
+
ServerSoftware: "RubyHome",
|
37
37
|
Logger: server_logger,
|
38
38
|
AccessLog: []
|
39
39
|
}
|
40
40
|
end
|
41
41
|
|
42
42
|
def server_logger
|
43
|
-
if ENV[
|
44
|
-
WEBrick::Log
|
45
|
-
elsif ENV[
|
46
|
-
WEBrick::Log
|
43
|
+
if ENV["DEBUG"] == "debug"
|
44
|
+
WEBrick::Log.new($stdout, WEBrick::BasicLog::DEBUG)
|
45
|
+
elsif ENV["DEBUG"] == "info"
|
46
|
+
WEBrick::Log.new($stdout, WEBrick::BasicLog::INFO)
|
47
47
|
else
|
48
|
-
WEBrick::Log
|
48
|
+
WEBrick::Log.new("/dev/null", WEBrick::BasicLog::WARN)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -61,5 +61,3 @@ module RubyHome
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
64
|
-
|
65
|
-
|
@@ -54,35 +54,35 @@ module RubyHome
|
|
54
54
|
|
55
55
|
private
|
56
56
|
|
57
|
-
|
57
|
+
attr_reader :socket, :encrypter_class, :decrypter_class
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
def received_encrypted_request?
|
60
|
+
@received_encrypted_request ||= false
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
def encrypter
|
64
|
+
@_encrypter ||= encrypter_class.new(accessory_to_controller_key)
|
65
|
+
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
def encryption_time?
|
68
|
+
accessory_to_controller_key? && received_encrypted_request?
|
69
|
+
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
def decrypter
|
72
|
+
@_decrypter ||= decrypter_class.new(controller_to_accessory_key)
|
73
|
+
end
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
def decryption_time?
|
76
|
+
!!controller_to_accessory_key
|
77
|
+
end
|
78
78
|
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def accessory_to_controller_key?
|
80
|
+
!!accessory_to_controller_key
|
81
|
+
end
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
def controller_to_accessory_key?
|
84
|
+
!!controller_to_accessory_key
|
85
|
+
end
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "facets/string/modulize"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
class BaseValue
|
@@ -8,7 +8,7 @@ module RubyHome
|
|
8
8
|
Object.const_get("::RubyHome::#{template.format.modulize}Value") || NullValue
|
9
9
|
end
|
10
10
|
|
11
|
-
def initialize(characteristic_template=nil, initial_value=nil)
|
11
|
+
def initialize(characteristic_template = nil, initial_value = nil)
|
12
12
|
@template = characteristic_template
|
13
13
|
@value = initial_value
|
14
14
|
end
|
@@ -25,6 +25,6 @@ module RubyHome
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
|
28
|
+
attr_reader :template
|
29
29
|
end
|
30
30
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "base_value"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
class BoolValue < BaseValue
|
5
5
|
REMAPPED_VALUES = {
|
6
|
-
|
6
|
+
"0" => false,
|
7
7
|
0 => false,
|
8
|
-
|
9
|
-
1 => true
|
8
|
+
"1" => true,
|
9
|
+
1 => true
|
10
10
|
}.freeze
|
11
11
|
|
12
12
|
def default
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "base_value"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
class FloatValue < BaseValue
|
@@ -8,8 +8,8 @@ module RubyHome
|
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def minimum_value
|
12
|
+
template.constraints.fetch("MinimumValue", 0)
|
13
|
+
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "base_value"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
class Int32Value < BaseValue
|
@@ -8,8 +8,8 @@ module RubyHome
|
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def minimum_value
|
12
|
+
template.constraints.fetch("MinimumValue")
|
13
|
+
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "base_value"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
class StringValue < BaseValue
|
5
5
|
DEFAULT_VALUES = {
|
6
|
-
firmware_revision:
|
7
|
-
hardware_revision:
|
8
|
-
manufacturer:
|
9
|
-
model:
|
10
|
-
name:
|
11
|
-
serial_number:
|
12
|
-
version:
|
6
|
+
firmware_revision: "1.0",
|
7
|
+
hardware_revision: "1.0",
|
8
|
+
manufacturer: "Default-Manufacturer",
|
9
|
+
model: "Default-Model",
|
10
|
+
name: "RubyHome",
|
11
|
+
serial_number: "Default-SerialNumber",
|
12
|
+
version: "1.0"
|
13
13
|
}.freeze
|
14
14
|
|
15
15
|
def default
|
@@ -18,8 +18,8 @@ module RubyHome
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def name
|
22
|
+
template.name
|
23
|
+
end
|
24
24
|
end
|
25
25
|
end
|