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,16 +1,22 @@
1
1
  module RubyHome
2
+ class UnknownCategoriyIdentifierError < StandardError; end
3
+
2
4
  class Configuration
3
5
  extend Forwardable
4
6
 
7
+ CATEGORIES_FILEPATH = (File.dirname(__FILE__) + "/config/categories.yml").freeze
8
+ CATEGORIES = YAML.load_file(CATEGORIES_FILEPATH).freeze
9
+
5
10
  def initialize(accessory_info = AccessoryInfo.instance)
6
11
  @accessory_info = accessory_info
7
12
  end
8
13
 
9
- DEFAULT_NAME = -'RubyHome'
10
- DEFAULT_HOST = -'0.0.0.0'
14
+ DEFAULT_NAME = -"RubyHome"
15
+ DEFAULT_HOST = -"0.0.0.0"
11
16
  DEFAULT_PORT = 4567
12
17
  DEFAULT_MODEL_NAME = DEFAULT_NAME
13
18
  DEFAULT_DISCOVERY_NAME = DEFAULT_NAME
19
+ DEFAULT_CATEGORY_IDENTIFIER = 2
14
20
 
15
21
  def discovery_name
16
22
  @discovery_name || DEFAULT_DISCOVERY_NAME
@@ -28,6 +34,19 @@ module RubyHome
28
34
  @host || DEFAULT_HOST
29
35
  end
30
36
 
37
+ def category_identifier
38
+ @category_identifier || DEFAULT_CATEGORY_IDENTIFIER
39
+ end
40
+
41
+ def category_identifier=(value)
42
+ if value.is_a?(Symbol)
43
+ raise UnknownCategoriyIdentifierError unless CATEGORIES.include?(value)
44
+ @category_identifier = CATEGORIES[value]
45
+ else
46
+ @category_identifier = value.to_i
47
+ end
48
+ end
49
+
31
50
  attr_writer :discovery_name, :model_name, :host, :port
32
51
 
33
52
  def_delegators :@accessory_info, :password, :password=
@@ -1,12 +1,12 @@
1
1
  module RubyHome
2
2
  module DeviceID
3
3
  class << self
4
- DELIMITER = ':'
4
+ DELIMITER = ":"
5
5
 
6
6
  # Device ID of the accessory must be a unique random number generated at every
7
7
  # factory reset and must persist across reboots
8
8
 
9
- def generate(digits=12)
9
+ def generate(digits = 12)
10
10
  random_hexadecimals(digits)
11
11
  .map(&:upcase)
12
12
  .each_slice(2)
@@ -18,7 +18,7 @@ module RubyHome
18
18
  rand(15).to_s(16)
19
19
  end
20
20
 
21
- def random_hexadecimals(digits=12)
21
+ def random_hexadecimals(digits = 12)
22
22
  digits
23
23
  .times
24
24
  .map { random_hexadecimal }
@@ -1,12 +1,12 @@
1
1
  module RubyHome
2
2
  module DNS
3
3
  class Service
4
- def initialize(configuration: )
4
+ def initialize(configuration:)
5
5
  @configuration = configuration
6
6
  end
7
7
 
8
8
  def update
9
- return if RbConfig::CONFIG['target_os'] =~ /linux/
9
+ return if /linux/.match?(RbConfig::CONFIG["target_os"])
10
10
 
11
11
  dnssd_service.add_record(DNSSD::Record::TXT, text_record.encode)
12
12
  end
@@ -20,9 +20,7 @@ module RubyHome
20
20
  attr_reader :configuration
21
21
 
22
22
  def dnssd_service
23
- @_dnssd_service ||= begin
24
- DNSSD::Service.register(name, type, nil, port, host, text_record)
25
- end
23
+ @_dnssd_service ||= DNSSD::Service.register(name, type, nil, port, host, text_record)
26
24
  end
27
25
 
28
26
  def name
@@ -30,7 +28,7 @@ module RubyHome
30
28
  end
31
29
 
32
30
  def type
33
- -'_hap._tcp'
31
+ -"_hap._tcp"
34
32
  end
35
33
 
36
34
  def port
@@ -12,14 +12,14 @@ module RubyHome
12
12
 
13
13
  def to_hash
14
14
  {
15
- 'c#' => current_configuration_number,
16
- 'ci' => accessory_category_identifier,
17
- 'ff' => feature_flags,
18
- 'id' => device_id,
19
- 'md' => model_name,
20
- 'pv' => protocol_version,
21
- 's#' => current_state_number,
22
- 'sf' => status_flags
15
+ "c#" => current_configuration_number,
16
+ "ci" => accessory_category_identifier,
17
+ "ff" => feature_flags,
18
+ "id" => device_id,
19
+ "md" => model_name,
20
+ "pv" => protocol_version,
21
+ "s#" => current_state_number,
22
+ "sf" => status_flags
23
23
  }
24
24
  end
25
25
 
@@ -38,7 +38,7 @@ module RubyHome
38
38
  # 1-65535.
39
39
 
40
40
  def accessory_category_identifier
41
- 2
41
+ @configuration.category_identifier
42
42
  end
43
43
 
44
44
  # Feature flags (e.g. "0x3" for bits 0 and 1). Required if non-zero.
@@ -52,7 +52,7 @@ module RubyHome
52
52
 
53
53
  STATUS_FLAGS = {
54
54
  PAIRED: 0,
55
- NOT_PAIRED: 1,
55
+ NOT_PAIRED: 1
56
56
  }.freeze
57
57
 
58
58
  def status_flags
@@ -1,4 +1,5 @@
1
1
  module RubyHome
2
2
  class RubyHomeError < StandardError; end
3
+
3
4
  class UnknownValueError < RubyHomeError; end
4
5
  end
@@ -1,6 +1,6 @@
1
1
  module RubyHome
2
2
  class CharacteristicFactory
3
- def self.create(characteristic_name, service: , subtype: 'default' , value: nil)
3
+ def self.create(characteristic_name, service:, subtype: "default", value: nil)
4
4
  new(
5
5
  characteristic_name: characteristic_name,
6
6
  service: service,
@@ -15,6 +15,7 @@ module RubyHome
15
15
  format: template.format,
16
16
  name: characteristic_name,
17
17
  properties: template.properties,
18
+ constraints: template.constraints,
18
19
  service: service,
19
20
  unit: template.unit,
20
21
  uuid: template.uuid,
@@ -35,48 +36,48 @@ module RubyHome
35
36
 
36
37
  private
37
38
 
38
- def initialize(characteristic_name:, service:, subtype:, value:)
39
- @characteristic_name = characteristic_name.to_sym
40
- @service = service
41
- @subtype = subtype
42
- @requested_value = value
43
- end
39
+ def initialize(characteristic_name:, service:, subtype:, value:)
40
+ @characteristic_name = characteristic_name.to_sym
41
+ @service = service
42
+ @subtype = subtype
43
+ @requested_value = value
44
+ end
44
45
 
45
- attr_reader :service, :characteristic_name, :subtype
46
+ attr_reader :service, :characteristic_name, :subtype
46
47
 
47
- def accessory
48
- service.accessory
49
- end
48
+ def accessory
49
+ service.accessory
50
+ end
50
51
 
51
- def template
52
- @template ||= CharacteristicTemplate.find_by(name: characteristic_name)
53
- end
52
+ def template
53
+ @template ||= CharacteristicTemplate.find_by(name: characteristic_name)
54
+ end
54
55
 
55
- def value_object
56
- value_object_class.new(template, @requested_value)
57
- end
56
+ def value_object
57
+ value_object_class.new(template, @requested_value)
58
+ end
58
59
 
59
- def value_object_class
60
- BaseValue.value_for_template(template)
61
- end
60
+ def value_object_class
61
+ BaseValue.value_for_template(template)
62
+ end
62
63
 
63
- def persisted_characteristic
64
- IdentifierCache.find_by(
65
- accessory_id: service.accessory_id,
66
- service_uuid: service.uuid,
67
- uuid: template.uuid,
68
- subtype: subtype
69
- )
70
- end
64
+ def persisted_characteristic
65
+ IdentifierCache.find_by(
66
+ accessory_id: service.accessory_id,
67
+ service_uuid: service.uuid,
68
+ uuid: template.uuid,
69
+ subtype: subtype
70
+ )
71
+ end
71
72
 
72
- def persist_characteristic(characteristic)
73
- IdentifierCache.create(
74
- accessory_id: characteristic.accessory_id,
75
- instance_id: characteristic.instance_id,
76
- service_uuid: service.uuid,
77
- uuid: characteristic.uuid,
78
- subtype: subtype
79
- )
80
- end
73
+ def persist_characteristic(characteristic)
74
+ IdentifierCache.create(
75
+ accessory_id: characteristic.accessory_id,
76
+ instance_id: characteristic.instance_id,
77
+ service_uuid: service.uuid,
78
+ uuid: characteristic.uuid,
79
+ subtype: subtype
80
+ )
81
+ end
81
82
  end
82
83
  end
@@ -1,6 +1,6 @@
1
1
  module RubyHome
2
2
  class ServiceFactory
3
- def self.create(service_name, accessory: Accessory.new, subtype: 'default', **options)
3
+ def self.create(service_name, accessory: Accessory.new, subtype: "default", **options)
4
4
  new(
5
5
  service_name: service_name,
6
6
  accessory: accessory,
@@ -14,7 +14,7 @@ module RubyHome
14
14
  accessory: accessory,
15
15
  description: template.description,
16
16
  name: service_name,
17
- uuid: template.uuid,
17
+ uuid: template.uuid
18
18
  )
19
19
 
20
20
  if persisted_service
@@ -38,87 +38,87 @@ module RubyHome
38
38
 
39
39
  private
40
40
 
41
- ACCESSORY_INFORMATION_OPTIONS = [
42
- :firmware_revision,
43
- :manufacturer,
44
- :model,
45
- :name,
46
- :serial_number
47
- ].freeze
48
-
49
- def initialize(service_name:, accessory:, subtype:, **options)
50
- @service_name = service_name.to_sym
51
- @accessory = accessory
52
- @subtype = subtype
53
- @options = options
54
- end
41
+ ACCESSORY_INFORMATION_OPTIONS = [
42
+ :firmware_revision,
43
+ :manufacturer,
44
+ :model,
45
+ :name,
46
+ :serial_number
47
+ ].freeze
48
+
49
+ def initialize(service_name:, accessory:, subtype:, **options)
50
+ @service_name = service_name.to_sym
51
+ @accessory = accessory
52
+ @subtype = subtype
53
+ @options = options
54
+ end
55
55
 
56
- attr_reader :service_name, :accessory, :subtype, :options
57
-
58
- def create_required_characteristics(service)
59
- template.required_characteristics.each do |characteristic_template|
60
- characteristic_name = characteristic_template.name
61
- CharacteristicFactory.create(
62
- characteristic_template.name,
63
- service: service,
64
- subtype: subtype,
65
- value: options[characteristic_name]
66
- )
67
- end
68
- end
56
+ attr_reader :service_name, :accessory, :subtype, :options
69
57
 
70
- def create_optional_characteristics(service)
71
- optional_characteristic_templates.each do |characteristic_template|
72
- characteristic_name = characteristic_template.name
73
- CharacteristicFactory.create(
74
- characteristic_name,
75
- service: service,
76
- subtype: subtype,
77
- value: options[characteristic_name]
78
- )
79
- end
58
+ def create_required_characteristics(service)
59
+ template.required_characteristics.each do |characteristic_template|
60
+ characteristic_name = characteristic_template.name
61
+ CharacteristicFactory.create(
62
+ characteristic_template.name,
63
+ service: service,
64
+ subtype: subtype,
65
+ value: options[characteristic_name]
66
+ )
80
67
  end
68
+ end
81
69
 
82
- def optional_characteristic_templates
83
- template.optional_characteristics.select do |characteristic_template|
84
- options.keys.include?(characteristic_template.name)
85
- end
70
+ def create_optional_characteristics(service)
71
+ optional_characteristic_templates.each do |characteristic_template|
72
+ characteristic_name = characteristic_template.name
73
+ CharacteristicFactory.create(
74
+ characteristic_name,
75
+ service: service,
76
+ subtype: subtype,
77
+ value: options[characteristic_name]
78
+ )
86
79
  end
80
+ end
87
81
 
88
- def accessory_information_factory?
89
- service_name == :accessory_information
82
+ def optional_characteristic_templates
83
+ template.optional_characteristics.select do |characteristic_template|
84
+ options.key?(characteristic_template.name)
90
85
  end
86
+ end
91
87
 
92
- def create_accessory_information
93
- return if accessory.has_accessory_information?
88
+ def accessory_information_factory?
89
+ service_name == :accessory_information
90
+ end
94
91
 
95
- ServiceFactory.create(
96
- :accessory_information,
97
- accessory: accessory,
98
- subtype: 'accessory_information',
99
- **options.slice(*ACCESSORY_INFORMATION_OPTIONS)
100
- )
101
- end
92
+ def create_accessory_information
93
+ return if accessory.has_accessory_information?
102
94
 
103
- def template
104
- @template ||= ServiceTemplate.find_by(name: service_name)
105
- end
95
+ ServiceFactory.create(
96
+ :accessory_information,
97
+ accessory: accessory,
98
+ subtype: "accessory_information",
99
+ **options.slice(*ACCESSORY_INFORMATION_OPTIONS)
100
+ )
101
+ end
106
102
 
107
- def persisted_service
108
- IdentifierCache.find_by(
109
- accessory_id: accessory.id,
110
- uuid: template.uuid,
111
- subtype: subtype
112
- )
113
- end
103
+ def template
104
+ @template ||= ServiceTemplate.find_by(name: service_name)
105
+ end
114
106
 
115
- def persist_service(service)
116
- IdentifierCache.create(
117
- accessory_id: accessory.id,
118
- instance_id: service.instance_id,
119
- uuid: template.uuid,
120
- subtype: subtype
121
- )
122
- end
107
+ def persisted_service
108
+ IdentifierCache.find_by(
109
+ accessory_id: accessory.id,
110
+ uuid: template.uuid,
111
+ subtype: subtype
112
+ )
113
+ end
114
+
115
+ def persist_service(service)
116
+ IdentifierCache.create(
117
+ accessory_id: accessory.id,
118
+ instance_id: service.instance_id,
119
+ uuid: template.uuid,
120
+ subtype: subtype
121
+ )
122
+ end
123
123
  end
124
124
  end
@@ -1,10 +1,11 @@
1
1
  module RubyHome
2
2
  class CharacteristicTemplate
3
- FILEPATH = (File.dirname(__FILE__) + '/../../config/characteristics.yml').freeze
4
- DATA = YAML.load_file(FILEPATH).freeze
3
+ FILENAMES = %w[characteristics.yml manual_characteristics.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,18 +16,17 @@ module RubyHome
15
16
  end
16
17
  end
17
18
 
18
- def initialize(name:, description:, uuid:, format:, unit:, permissions:, properties:, constraints: )
19
+ def initialize(name:, description:, uuid:, format:, unit:, properties:, constraints:)
19
20
  @name = name
20
21
  @description = description
21
22
  @uuid = uuid
22
23
  @format = format
23
24
  @unit = unit
24
- @permissions = permissions
25
25
  @properties = properties
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, :properties
30
30
 
31
31
  def constraints
32
32
  @constraints || {}