ruby_home 0.2.1 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests.yml +41 -0
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +2 -9
  5. data/.standard.yml +3 -0
  6. data/CHANGELOG.md +18 -0
  7. data/Gemfile +2 -2
  8. data/README.md +40 -77
  9. data/Rakefile +4 -4
  10. data/examples/air_purifier.rb +66 -0
  11. data/examples/air_quality_sensor.rb +119 -0
  12. data/examples/battery_service.rb +58 -0
  13. data/examples/carbon_dioxide_sensor.rb +78 -0
  14. data/examples/carbon_monoxide_sensor.rb +78 -0
  15. data/examples/contact_sensor.rb +66 -0
  16. data/examples/door.rb +48 -0
  17. data/examples/fan.rb +30 -0
  18. data/examples/fan_v2.rb +68 -0
  19. data/examples/garage_door_opener.rb +71 -0
  20. data/examples/heater_cooler.rb +92 -0
  21. data/examples/humidifier_dehumidifier.rb +88 -0
  22. data/examples/humidity_sensor.rb +62 -0
  23. data/examples/leak_sensor.rb +66 -0
  24. data/examples/light_sensor.rb +64 -0
  25. data/examples/lightbulb.rb +31 -0
  26. data/examples/lock_mechanism.rb +34 -0
  27. data/examples/motion_sensor.rb +66 -0
  28. data/examples/occupancy_sensor.rb +66 -0
  29. data/examples/outlet.rb +29 -0
  30. data/examples/security_system.rb +53 -0
  31. data/examples/smoke_sensor.rb +66 -0
  32. data/examples/switch.rb +16 -0
  33. data/examples/television.rb +73 -0
  34. data/examples/temperature_sensor.rb +62 -0
  35. data/examples/thermostat.rb +113 -0
  36. data/examples/window.rb +48 -0
  37. data/examples/window_covering.rb +72 -0
  38. data/lib/ruby_home.rb +21 -22
  39. data/lib/ruby_home/accessory.rb +1 -1
  40. data/lib/ruby_home/accessory_collection.rb +6 -6
  41. data/lib/ruby_home/accessory_info.rb +18 -18
  42. data/lib/ruby_home/characteristic.rb +13 -7
  43. data/lib/ruby_home/characteristic_collection.rb +13 -13
  44. data/lib/ruby_home/config/categories.yml +39 -0
  45. data/lib/ruby_home/config/characteristics.yml +190 -319
  46. data/lib/ruby_home/config/manual_characteristics.yml +278 -0
  47. data/lib/ruby_home/config/manual_services.yml +45 -0
  48. data/lib/ruby_home/config/services.yml +338 -304
  49. data/lib/ruby_home/configuration.rb +21 -2
  50. data/lib/ruby_home/device_id.rb +3 -3
  51. data/lib/ruby_home/dns/service.rb +4 -6
  52. data/lib/ruby_home/dns/text_record.rb +10 -10
  53. data/lib/ruby_home/errors.rb +1 -0
  54. data/lib/ruby_home/factories/characteristic_factory.rb +38 -37
  55. data/lib/ruby_home/factories/service_factory.rb +72 -72
  56. data/lib/ruby_home/factories/templates/characteristic_template.rb +6 -6
  57. data/lib/ruby_home/factories/templates/service_template.rb +12 -11
  58. data/lib/ruby_home/hap/crypto/chacha20poly1305.rb +2 -2
  59. data/lib/ruby_home/hap/crypto/hkdf.rb +4 -4
  60. data/lib/ruby_home/hap/crypto/session_key.rb +7 -7
  61. data/lib/ruby_home/hap/decrypter.rb +8 -8
  62. data/lib/ruby_home/hap/encrypter.rb +5 -6
  63. data/lib/ruby_home/hap/ev_response.rb +17 -17
  64. data/lib/ruby_home/hap/hap_request.rb +1 -1
  65. data/lib/ruby_home/hap/server.rb +4 -4
  66. data/lib/ruby_home/hap/server_handler.rb +8 -10
  67. data/lib/ruby_home/hap/session.rb +22 -22
  68. data/lib/ruby_home/hap/values/base_value.rb +3 -3
  69. data/lib/ruby_home/hap/values/bool_value.rb +4 -4
  70. data/lib/ruby_home/hap/values/float_value.rb +4 -4
  71. data/lib/ruby_home/hap/values/identify_value.rb +1 -1
  72. data/lib/ruby_home/hap/values/int32_value.rb +4 -4
  73. data/lib/ruby_home/hap/values/null_value.rb +1 -1
  74. data/lib/ruby_home/hap/values/string_value.rb +11 -11
  75. data/lib/ruby_home/hap/values/uint32_value.rb +5 -5
  76. data/lib/ruby_home/hap/values/uint8_value.rb +14 -14
  77. data/lib/ruby_home/hex_helper.rb +3 -3
  78. data/lib/ruby_home/http/application.rb +7 -7
  79. data/lib/ruby_home/http/controllers/accessories_controller.rb +3 -3
  80. data/lib/ruby_home/http/controllers/application_controller.rb +6 -6
  81. data/lib/ruby_home/http/controllers/characteristics_controller.rb +29 -29
  82. data/lib/ruby_home/http/controllers/identify_controller.rb +10 -10
  83. data/lib/ruby_home/http/controllers/pair_setups_controller.rb +19 -19
  84. data/lib/ruby_home/http/controllers/pair_verifies_controller.rb +9 -9
  85. data/lib/ruby_home/http/controllers/pairings_controller.rb +6 -6
  86. data/lib/ruby_home/http/serializers/accessory_serializer.rb +5 -5
  87. data/lib/ruby_home/http/serializers/characteristic_serializer.rb +27 -19
  88. data/lib/ruby_home/http/serializers/characteristic_value_serializer.rb +5 -5
  89. data/lib/ruby_home/http/serializers/object_serializer.rb +1 -1
  90. data/lib/ruby_home/http/serializers/service_serializer.rb +19 -9
  91. data/lib/ruby_home/http/serializers/uuid_helper.rb +2 -2
  92. data/lib/ruby_home/http/services/session_notifier.rb +8 -8
  93. data/lib/ruby_home/http/services/start_srp_service.rb +3 -3
  94. data/lib/ruby_home/http/services/verify_finish_service.rb +22 -22
  95. data/lib/ruby_home/http/services/verify_srp_service.rb +22 -22
  96. data/lib/ruby_home/identifier_cache.rb +4 -5
  97. data/lib/ruby_home/password.rb +14 -14
  98. data/lib/ruby_home/persistable.rb +19 -12
  99. data/lib/ruby_home/service.rb +5 -2
  100. data/lib/ruby_home/service_collection.rb +1 -1
  101. data/lib/ruby_home/version.rb +1 -1
  102. data/rubyhome.gemspec +31 -31
  103. data/sbin/characteristic_generator.rb +22 -24
  104. data/sbin/service_generator.rb +38 -19
  105. metadata +85 -44
  106. data/.travis.yml +0 -31
@@ -1,10 +1,11 @@
1
1
  module RubyHome
2
2
  class ServiceTemplate
3
- FILEPATH = (File.dirname(__FILE__) + '/../../config/services.yml').freeze
4
- DATA = YAML.load_file(FILEPATH).freeze
3
+ FILENAMES = %w[services.yml manual_services.yml].freeze
4
+ FILEPATHS = FILENAMES.map { |filename| File.join(__dir__, "..", "..", "config", filename) }.freeze
5
+ DATA = FILEPATHS.flat_map { |filepath| YAML.load_file(filepath) }.freeze
5
6
 
6
7
  def self.all
7
- @all ||= DATA.map { |data| new(data) }
8
+ @all ||= DATA.map { |data| new(**data) }
8
9
  end
9
10
 
10
11
  def self.find_by(options)
@@ -15,25 +16,25 @@ module RubyHome
15
16
  end
16
17
  end
17
18
 
18
- def initialize(name:, description:, uuid:, optional_characteristics_uuids:, required_characteristics_uuids:)
19
+ def initialize(name:, description:, uuid:, optional_characteristic_names:, required_characteristic_names:)
19
20
  @name = name
20
21
  @description = description
21
22
  @uuid = uuid
22
- @optional_characteristics_uuids = optional_characteristics_uuids
23
- @required_characteristics_uuids = required_characteristics_uuids
23
+ @optional_characteristic_names = optional_characteristic_names
24
+ @required_characteristic_names = required_characteristic_names
24
25
  end
25
26
 
26
- attr_reader :name, :description, :uuid, :optional_characteristics_uuids, :required_characteristics_uuids
27
+ attr_reader :name, :description, :uuid, :optional_characteristic_names, :required_characteristic_names
27
28
 
28
29
  def optional_characteristics
29
- @optional_characteristics ||= optional_characteristics_uuids.map do |uuid|
30
- CharacteristicTemplate.find_by(uuid: uuid)
30
+ @optional_characteristics ||= optional_characteristic_names.map do |name|
31
+ CharacteristicTemplate.find_by(name: name)
31
32
  end
32
33
  end
33
34
 
34
35
  def required_characteristics
35
- @required_characteristics ||= required_characteristics_uuids.map do |uuid|
36
- CharacteristicTemplate.find_by(uuid: uuid)
36
+ @required_characteristics ||= required_characteristic_names.map do |name|
37
+ CharacteristicTemplate.find_by(name: name)
37
38
  end
38
39
  end
39
40
  end
@@ -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).next_bytes(BYTE_LENGTH)
14
+ GEM_HKDF.new(byte_string, hkdf_opts).read(BYTE_LENGTH)
15
15
  end
16
16
 
17
17
  private
18
18
 
19
- ALGORITHM = 'SHA512'
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('H*')
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
- SALT = -'Control-Salt'
20
- READ = -'Control-Read-Encryption-Key'
21
- WRITE = -'Control-Write-Encryption-Key'
19
+ SALT = -"Control-Salt"
20
+ READ = -"Control-Read-Encryption-Key"
21
+ WRITE = -"Control-Write-Encryption-Key"
22
22
 
23
- attr_reader :shared_secret
23
+ attr_reader :shared_secret
24
24
 
25
- def generate_shared_secret_key(info)
26
- Crypto::HKDF.new(info: info, salt: SALT).encrypt(shared_secret)
27
- end
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.freeze
5
- AUTHENTICATE_TAG_LENGTH_BYTES = 16.freeze
6
- NONCE_32_BIT_FIX_COMMENT_PART = [0].pack('L').freeze
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('v')
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('Q')
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.freeze
6
- NONCE_32_BIT_FIX_COMMENT_PART = [0].pack('L').freeze
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('v')
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('Q')
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
- attr_reader :body, :session
16
+ attr_reader :body, :session
17
17
 
18
- CRLF = -"\x0d\x0a"
19
- STATUS_LINE = -'EVENT/1.0 200 OK'
20
- CONTENT_TYPE_LINE = -'Content-Type: application/hap+json'
18
+ CRLF = -"\x0d\x0a"
19
+ STATUS_LINE = -"EVENT/1.0 200 OK"
20
+ CONTENT_TYPE_LINE = -"Content-Type: application/hap+json"
21
21
 
22
- def content_length_line
23
- "Content-Length: #{body.length}"
24
- end
22
+ def content_length_line
23
+ "Content-Length: #{body.length}"
24
+ end
25
25
 
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
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
- io.write(data)
33
- end
32
+ io.write(data)
33
+ end
34
34
 
35
- def send_body(io)
36
- io.write(body)
37
- end
35
+ def send_body(io)
36
+ io.write(body)
37
+ end
38
38
  end
39
39
  end
40
40
  end
@@ -8,7 +8,7 @@ module RubyHome
8
8
 
9
9
  def meta_vars
10
10
  super.merge(
11
- { "REQUEST_SESSION" => @session }
11
+ {"REQUEST_SESSION" => @session}
12
12
  )
13
13
  end
14
14
  end
@@ -4,11 +4,11 @@ module RubyHome
4
4
  def run(sock)
5
5
  session = Session.new(sock)
6
6
 
7
- while true
7
+ loop do
8
8
  req = HAPRequest.new(@config, session: session)
9
9
  res = HAPResponse.new(@config)
10
10
  begin
11
- while true
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 StandardError => ex
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: 'RubyHome',
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['DEBUG'] == 'debug'
44
- WEBrick::Log::new(STDOUT, WEBrick::BasicLog::DEBUG)
45
- elsif ENV['DEBUG'] == 'info'
46
- WEBrick::Log::new(STDOUT, WEBrick::BasicLog::INFO)
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::new("/dev/null", WEBrick::BasicLog::WARN)
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
- attr_reader :socket, :encrypter_class, :decrypter_class
57
+ attr_reader :socket, :encrypter_class, :decrypter_class
58
58
 
59
- def received_encrypted_request?
60
- @received_encrypted_request ||= false
61
- end
59
+ def received_encrypted_request?
60
+ @received_encrypted_request ||= false
61
+ end
62
62
 
63
- def encrypter
64
- @_encrypter ||= encrypter_class.new(accessory_to_controller_key)
65
- end
63
+ def encrypter
64
+ @_encrypter ||= encrypter_class.new(accessory_to_controller_key)
65
+ end
66
66
 
67
- def encryption_time?
68
- accessory_to_controller_key? && received_encrypted_request?
69
- end
67
+ def encryption_time?
68
+ accessory_to_controller_key? && received_encrypted_request?
69
+ end
70
70
 
71
- def decrypter
72
- @_decrypter ||= decrypter_class.new(controller_to_accessory_key)
73
- end
71
+ def decrypter
72
+ @_decrypter ||= decrypter_class.new(controller_to_accessory_key)
73
+ end
74
74
 
75
- def decryption_time?
76
- !!controller_to_accessory_key
77
- end
75
+ def decryption_time?
76
+ !!controller_to_accessory_key
77
+ end
78
78
 
79
- def accessory_to_controller_key?
80
- !!accessory_to_controller_key
81
- end
79
+ def accessory_to_controller_key?
80
+ !!accessory_to_controller_key
81
+ end
82
82
 
83
- def controller_to_accessory_key?
84
- !!controller_to_accessory_key
85
- end
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 'facets/string/modulize'
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
- attr_reader :template
28
+ attr_reader :template
29
29
  end
30
30
  end
@@ -1,12 +1,12 @@
1
- require_relative 'base_value'
1
+ require_relative "base_value"
2
2
 
3
3
  module RubyHome
4
4
  class BoolValue < BaseValue
5
5
  REMAPPED_VALUES = {
6
- '0' => false,
6
+ "0" => false,
7
7
  0 => false,
8
- '1' => true,
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 'base_value'
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
- def minimum_value
12
- template.constraints.fetch('MinimumValue', 0)
13
- end
11
+ def minimum_value
12
+ template.constraints.fetch("MinimumValue", 0)
13
+ end
14
14
  end
15
15
  end