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.
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