ruby_home 0.2.1 → 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 +41 -0
- data/.gitignore +2 -2
- data/.rubocop.yml +2 -9
- data/.standard.yml +3 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +2 -2
- data/README.md +40 -77
- data/Rakefile +4 -4
- data/examples/air_purifier.rb +66 -0
- data/examples/air_quality_sensor.rb +119 -0
- data/examples/battery_service.rb +58 -0
- data/examples/carbon_dioxide_sensor.rb +78 -0
- data/examples/carbon_monoxide_sensor.rb +78 -0
- data/examples/contact_sensor.rb +66 -0
- data/examples/door.rb +48 -0
- data/examples/fan.rb +30 -0
- data/examples/fan_v2.rb +68 -0
- data/examples/garage_door_opener.rb +71 -0
- data/examples/heater_cooler.rb +92 -0
- data/examples/humidifier_dehumidifier.rb +88 -0
- data/examples/humidity_sensor.rb +62 -0
- data/examples/leak_sensor.rb +66 -0
- data/examples/light_sensor.rb +64 -0
- data/examples/lightbulb.rb +31 -0
- data/examples/lock_mechanism.rb +34 -0
- data/examples/motion_sensor.rb +66 -0
- data/examples/occupancy_sensor.rb +66 -0
- data/examples/outlet.rb +29 -0
- data/examples/security_system.rb +53 -0
- data/examples/smoke_sensor.rb +66 -0
- data/examples/switch.rb +16 -0
- data/examples/television.rb +73 -0
- data/examples/temperature_sensor.rb +62 -0
- data/examples/thermostat.rb +113 -0
- data/examples/window.rb +48 -0
- data/examples/window_covering.rb +72 -0
- data/lib/ruby_home.rb +21 -22
- 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 +13 -7
- data/lib/ruby_home/characteristic_collection.rb +13 -13
- data/lib/ruby_home/config/categories.yml +39 -0
- data/lib/ruby_home/config/characteristics.yml +190 -319
- 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 +338 -304
- data/lib/ruby_home/configuration.rb +21 -2
- 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 +10 -10
- data/lib/ruby_home/errors.rb +1 -0
- data/lib/ruby_home/factories/characteristic_factory.rb +38 -37
- data/lib/ruby_home/factories/service_factory.rb +72 -72
- data/lib/ruby_home/factories/templates/characteristic_template.rb +6 -6
- data/lib/ruby_home/factories/templates/service_template.rb +12 -11
- 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 +19 -19
- data/lib/ruby_home/http/controllers/pair_verifies_controller.rb +9 -9
- data/lib/ruby_home/http/controllers/pairings_controller.rb +6 -6
- data/lib/ruby_home/http/serializers/accessory_serializer.rb +5 -5
- data/lib/ruby_home/http/serializers/characteristic_serializer.rb +27 -19
- 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 +19 -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 +19 -12
- data/lib/ruby_home/service.rb +5 -2
- 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 +38 -19
- metadata +85 -44
- data/.travis.yml +0 -31
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "application_controller"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
module HTTP
|
5
5
|
class PairingsController < ApplicationController
|
6
|
-
post
|
7
|
-
content_type
|
6
|
+
post "/" do
|
7
|
+
content_type "application/pairing+tlv8"
|
8
8
|
|
9
9
|
case unpack_request[:method]
|
10
10
|
when 3
|
@@ -20,9 +20,9 @@ module RubyHome
|
|
20
20
|
pairing_params = {
|
21
21
|
admin: !!unpack_request[:permissions],
|
22
22
|
identifier: unpack_request[:identifier],
|
23
|
-
public_key: unpack_request[:public_key].unpack1(
|
23
|
+
public_key: unpack_request[:public_key].unpack1("H*")
|
24
24
|
}
|
25
|
-
accessory_info.add_paired_client
|
25
|
+
accessory_info.add_paired_client(**pairing_params)
|
26
26
|
|
27
27
|
tlv state: 2
|
28
28
|
end
|
@@ -30,7 +30,7 @@ module RubyHome
|
|
30
30
|
def remove_pairing
|
31
31
|
accessory_info.remove_paired_client(unpack_request[:identifier])
|
32
32
|
|
33
|
-
response[
|
33
|
+
response["connection"] = "close"
|
34
34
|
tlv state: 2
|
35
35
|
end
|
36
36
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
1
|
+
require_relative "object_serializer"
|
2
|
+
require_relative "service_serializer"
|
3
3
|
|
4
4
|
module RubyHome
|
5
5
|
module HTTP
|
@@ -7,13 +7,13 @@ module RubyHome
|
|
7
7
|
include ObjectSerializer
|
8
8
|
|
9
9
|
def root
|
10
|
-
|
10
|
+
"accessories"
|
11
11
|
end
|
12
12
|
|
13
13
|
def record_hash(accessory)
|
14
14
|
{
|
15
|
-
|
16
|
-
|
15
|
+
"aid" => accessory.id,
|
16
|
+
"services" => ServiceSerializer.new(accessory.services).serializable_hash
|
17
17
|
}
|
18
18
|
end
|
19
19
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
1
|
+
require_relative "object_serializer"
|
2
|
+
require_relative "uuid_helper"
|
3
3
|
|
4
4
|
module RubyHome
|
5
5
|
module HTTP
|
@@ -9,31 +9,39 @@ module RubyHome
|
|
9
9
|
|
10
10
|
def record_hash(characteristic)
|
11
11
|
{
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
12
|
+
"iid" => characteristic.instance_id,
|
13
|
+
"type" => uuid_short_form(characteristic.uuid),
|
14
|
+
"perms" => perms(characteristic),
|
15
|
+
"format" => characteristic.format,
|
16
|
+
"description" => characteristic.description
|
17
|
+
}
|
18
|
+
.merge(value_hash(characteristic))
|
19
|
+
.merge(valid_values_hash(characteristic))
|
18
20
|
end
|
19
21
|
|
20
22
|
private
|
21
23
|
|
22
|
-
|
24
|
+
def perms(characteristic)
|
25
|
+
characteristic.properties.map do |property|
|
26
|
+
RubyHome::Characteristic::PROPERTIES[property]
|
27
|
+
end.compact
|
28
|
+
end
|
23
29
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
def value_hash(characteristic)
|
31
|
+
if characteristic.properties.include?("read")
|
32
|
+
{"value" => characteristic.value}
|
33
|
+
else
|
34
|
+
{}
|
28
35
|
end
|
36
|
+
end
|
29
37
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
38
|
+
def valid_values_hash(characteristic)
|
39
|
+
if characteristic.valid_values.empty?
|
40
|
+
{}
|
41
|
+
else
|
42
|
+
{"valid-values" => characteristic.valid_values}
|
36
43
|
end
|
44
|
+
end
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "object_serializer"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
module HTTP
|
@@ -6,14 +6,14 @@ module RubyHome
|
|
6
6
|
include ObjectSerializer
|
7
7
|
|
8
8
|
def root
|
9
|
-
|
9
|
+
"characteristics"
|
10
10
|
end
|
11
11
|
|
12
12
|
def record_hash(characteristic)
|
13
13
|
{
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
"aid" => characteristic.accessory_id,
|
15
|
+
"iid" => characteristic.instance_id,
|
16
|
+
"value" => characteristic.value
|
17
17
|
}
|
18
18
|
end
|
19
19
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
3
|
-
require_relative
|
1
|
+
require_relative "characteristic_serializer"
|
2
|
+
require_relative "object_serializer"
|
3
|
+
require_relative "uuid_helper"
|
4
4
|
|
5
5
|
module RubyHome
|
6
6
|
module HTTP
|
@@ -10,12 +10,22 @@ module RubyHome
|
|
10
10
|
|
11
11
|
def record_hash(service)
|
12
12
|
{
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
}
|
13
|
+
"iid" => service.instance_id,
|
14
|
+
"type" => uuid_short_form(service.uuid),
|
15
|
+
"characteristics" => CharacteristicSerializer.new(service.characteristics).serializable_hash,
|
16
|
+
"primary" => service.primary,
|
17
|
+
"hidden" => service.hidden
|
18
|
+
}.merge(linked_services(service))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def linked_services(service)
|
24
|
+
if service.linked.empty?
|
25
|
+
{}
|
26
|
+
else
|
27
|
+
{"linked" => service.linked.map(&:instance_id)}
|
28
|
+
end
|
19
29
|
end
|
20
30
|
end
|
21
31
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RubyHome
|
2
2
|
module HTTP
|
3
3
|
module UUIDHelper
|
4
|
-
APPLE_BASE_UUID = -
|
4
|
+
APPLE_BASE_UUID = -"0000-1000-8000-0026BB765291"
|
5
5
|
|
6
6
|
def uuid_short_form(uuid)
|
7
7
|
return uuid unless apple_defined_uuid?(uuid)
|
@@ -14,7 +14,7 @@ module RubyHome
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def trim_leading_zeros(input)
|
17
|
-
input.gsub(/^0*/,
|
17
|
+
input.gsub(/^0*/, "")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -16,21 +16,21 @@ module RubyHome
|
|
16
16
|
|
17
17
|
def ==(other)
|
18
18
|
self.class == other.class &&
|
19
|
-
|
20
|
-
|
19
|
+
session == other.session &&
|
20
|
+
characteristic == other.characteristic
|
21
21
|
end
|
22
22
|
|
23
23
|
attr_reader :session, :characteristic
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def send_ev_response
|
28
|
+
HAP::EVResponse.new(session, serialized_characteristic).send_response
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
def serialized_characteristic
|
32
|
+
HTTP::CharacteristicValueSerializer.new([characteristic]).serialized_json
|
33
|
+
end
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module RubyHome
|
2
2
|
class StartSRPService
|
3
|
-
def initialize(username
|
3
|
+
def initialize(username:, password:)
|
4
4
|
@username = username
|
5
5
|
@password = password
|
6
6
|
end
|
7
7
|
|
8
8
|
def salt_bytes
|
9
|
-
[salt].pack(
|
9
|
+
[salt].pack("H*")
|
10
10
|
end
|
11
11
|
|
12
12
|
def public_key_bytes
|
13
|
-
[public_key].pack(
|
13
|
+
[public_key].pack("H*")
|
14
14
|
end
|
15
15
|
|
16
16
|
def proof
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module RubyHome
|
2
2
|
class VerifyFinishService
|
3
|
-
def initialize(accessory_info
|
3
|
+
def initialize(accessory_info:, encrypted_data:, session_key:, shared_secret:)
|
4
4
|
@accessory_info = accessory_info
|
5
5
|
@encrypted_data = encrypted_data
|
6
6
|
@session_key = session_key
|
@@ -23,34 +23,34 @@ module RubyHome
|
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
|
-
|
26
|
+
NONCE = -"PV-Msg03"
|
27
27
|
|
28
|
-
|
28
|
+
attr_reader :accessory_info, :encrypted_data, :session_key, :shared_secret
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
30
|
+
def paired_client_exists?
|
31
|
+
accessory_info.paired_clients.any? do |paired_client|
|
32
|
+
paired_client[:identifier] == identifier
|
34
33
|
end
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
def chacha20poly1305
|
37
|
+
HAP::Crypto::ChaCha20Poly1305.new(session_key)
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
def nonce
|
41
|
+
HexHelper.pad(NONCE)
|
42
|
+
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
def decrypted_data
|
45
|
+
chacha20poly1305.decrypt(nonce, encrypted_data)
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
def unpacked_decrypted_data
|
49
|
+
TLV.decode(decrypted_data)
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
def identifier
|
53
|
+
unpacked_decrypted_data[:identifier]
|
54
|
+
end
|
55
55
|
end
|
56
56
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module RubyHome
|
2
2
|
class VerifySRPService
|
3
|
-
def initialize(device_proof
|
4
|
-
@device_proof_bytes = device_proof.to_s.unpack1(
|
5
|
-
@public_key_bytes = public_key.to_s.unpack1(
|
3
|
+
def initialize(device_proof:, public_key:, srp_session:)
|
4
|
+
@device_proof_bytes = device_proof.to_s.unpack1("H*")
|
5
|
+
@public_key_bytes = public_key.to_s.unpack1("H*")
|
6
6
|
@srp_session = srp_session
|
7
7
|
end
|
8
8
|
|
@@ -20,29 +20,29 @@ module RubyHome
|
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
def valid_session?
|
24
|
+
!!verify_session
|
25
|
+
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def session_key
|
28
|
+
srp_verifier.K
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
def verify_session_bytes
|
32
|
+
[verify_session].pack("H*")
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
def verify_session
|
36
|
+
srp_verifier.verify_session(
|
37
|
+
srp_session.merge({A: public_key_bytes}),
|
38
|
+
device_proof_bytes
|
39
|
+
)
|
40
|
+
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
def srp_verifier
|
43
|
+
@_verifier ||= RubyHome::SRP::Verifier.new
|
44
|
+
end
|
45
45
|
|
46
|
-
|
46
|
+
attr_reader :public_key_bytes, :device_proof_bytes, :srp_session
|
47
47
|
end
|
48
48
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "persistable"
|
2
2
|
|
3
3
|
module RubyHome
|
4
4
|
class IdentifierCache
|
5
5
|
include Persistable
|
6
6
|
|
7
|
-
self.source =
|
7
|
+
self.source = "identifier_cache.yml"
|
8
8
|
|
9
9
|
def self.all
|
10
10
|
raw_items = read || []
|
@@ -33,7 +33,7 @@ module RubyHome
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def initialize(accessory_id:, instance_id:,
|
36
|
+
def initialize(accessory_id:, instance_id:, subtype:, uuid:, service_uuid: nil)
|
37
37
|
@accessory_id = accessory_id
|
38
38
|
@instance_id = instance_id
|
39
39
|
@subtype = subtype
|
@@ -52,10 +52,9 @@ module RubyHome
|
|
52
52
|
instance_id: identifier.instance_id,
|
53
53
|
subtype: identifier.subtype,
|
54
54
|
service_uuid: identifier.service_uuid,
|
55
|
-
uuid: identifier.uuid
|
55
|
+
uuid: identifier.uuid
|
56
56
|
}
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
data/lib/ruby_home/password.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
module RubyHome
|
2
2
|
module Password
|
3
3
|
class << self
|
4
|
-
DELIMITER =
|
4
|
+
DELIMITER = "-"
|
5
5
|
LENGTH = 8
|
6
6
|
INVALIDS = [
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
"00000000",
|
8
|
+
"11111111",
|
9
|
+
"22222222",
|
10
|
+
"33333333",
|
11
|
+
"44444444",
|
12
|
+
"55555555",
|
13
|
+
"66666666",
|
14
|
+
"77777777",
|
15
|
+
"88888888",
|
16
|
+
"99999999",
|
17
|
+
"12345678",
|
18
|
+
"87654321"
|
19
19
|
]
|
20
20
|
|
21
21
|
# The Setup Code must conform to the format XXX-XX-XXX where each X is a
|
@@ -34,7 +34,7 @@ module RubyHome
|
|
34
34
|
[
|
35
35
|
digits[0...3],
|
36
36
|
digits[3...5],
|
37
|
-
digits[5...8]
|
37
|
+
digits[5...8]
|
38
38
|
].join(DELIMITER)
|
39
39
|
end
|
40
40
|
|