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,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 || {}