hue 0.3.0 → 0.3.2

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.
data/lib/hue/errors.rb CHANGED
@@ -2,33 +2,47 @@ module Hue
2
2
  class Error < StandardError; end
3
3
 
4
4
  class UnauthorizedUser < Error; end
5
+
5
6
  class InvalidJSON < Error; end
7
+
6
8
  class ResourceNotAvailable < Error; end
9
+
7
10
  class MethodNotAvailable < Error; end
11
+
8
12
  class MissingBody < Error; end
13
+
9
14
  class ParameterNotAvailable < Error; end
15
+
10
16
  class InvalidValueForParameter < Error; end
17
+
11
18
  class ParameterNotModifiable < Error; end
19
+
12
20
  class InternalError < Error; end
21
+
13
22
  class LinkButtonNotPressed < Error; end
23
+
14
24
  class ParameterNotModifiableWhileOff < ParameterNotModifiable; end
25
+
15
26
  class TooManyGroups < Error; end
27
+
16
28
  class GroupTooFull < Error; end
17
29
 
18
30
  class InvalidUsername < Error; end
31
+
19
32
  class UnknownError < Error; end
33
+
20
34
  class NoBridgeFound < Error; end
21
35
 
22
36
  # Status code to exception map
23
37
  ERROR_MAP = {
24
- 1 => Hue::UnauthorizedUser,
25
- 2 => Hue::InvalidJSON,
26
- 3 => Hue::ResourceNotAvailable,
27
- 4 => Hue::MethodNotAvailable,
28
- 5 => Hue::MissingBody,
29
- 6 => Hue::ParameterNotAvailable,
30
- 7 => Hue::InvalidValueForParameter,
31
- 8 => Hue::ParameterNotModifiable,
38
+ 1 => Hue::UnauthorizedUser,
39
+ 2 => Hue::InvalidJSON,
40
+ 3 => Hue::ResourceNotAvailable,
41
+ 4 => Hue::MethodNotAvailable,
42
+ 5 => Hue::MissingBody,
43
+ 6 => Hue::ParameterNotAvailable,
44
+ 7 => Hue::InvalidValueForParameter,
45
+ 8 => Hue::ParameterNotModifiable,
32
46
  901 => Hue::InternalError,
33
47
  101 => Hue::LinkButtonNotPressed,
34
48
  201 => Hue::ParameterNotModifiableWhileOff,
data/lib/hue/group.rb CHANGED
@@ -11,19 +11,19 @@ module Hue
11
11
  attr_reader :bridge
12
12
 
13
13
  # A unique, editable name given to the group.
14
- attr_accessor :name
14
+ attr_reader :name
15
15
 
16
16
  # Hue of the group. This is a wrapping value between 0 and 65535.
17
17
  # Both 0 and 65535 are red, 25500 is green and 46920 is blue.
18
18
  attr_accessor :hue
19
19
 
20
- # Saturation of the group. 255 is the most saturated (colored)
20
+ # Saturation of the group. 254 is the most saturated (colored)
21
21
  # and 0 is the least saturated (white).
22
22
  attr_accessor :saturation
23
23
 
24
24
  # Brightness of the group. This is a scale from the minimum
25
25
  # brightness the group is capable of, 0, to the maximum capable
26
- # brightness, 255. Note a brightness of 0 is not off.
26
+ # brightness, 254. Note a brightness of 0 is not off.
27
27
  attr_accessor :brightness
28
28
 
29
29
  # The x coordinate of a color in CIE color space. Between 0 and 1.
@@ -58,16 +58,14 @@ module Hue
58
58
  end
59
59
 
60
60
  def lights
61
- @lights ||= begin
62
- @light_ids.map do |light_id|
63
- @client.light(light_id)
64
- end
61
+ @lights ||= @light_ids.map do |light_id|
62
+ @client.light(light_id)
65
63
  end
66
64
  end
67
65
 
68
66
  def name=(name)
69
- resp = set_group_state({:name => name})
70
- @name = new? ? name : resp[0]['success']["/groups/#{id}/name"]
67
+ resp = set_group_state({name: name})
68
+ @name = new? ? name : resp[0]["success"]["/groups/#{id}/name"]
71
69
  end
72
70
 
73
71
  def lights=(light_ids)
@@ -78,17 +76,17 @@ module Hue
78
76
  @light_ids = light_ids.uniq
79
77
  @lights = nil # resets the memoization
80
78
 
81
- set_group_state({:lights => @light_ids})
79
+ set_group_state({lights: @light_ids})
82
80
  end
83
81
 
84
82
  def scene=(scene)
85
83
  scene_id = scene.is_a?(Scene) ? scene.id : scene
86
- set_group_state({:scene => scene_id})
84
+ set_group_state({scene: scene_id})
87
85
  end
88
86
 
89
87
  def <<(light_id)
90
88
  @light_ids << light_id
91
- set_group_state({:lights => @light_ids})
89
+ set_group_state({lights: @light_ids})
92
90
  end
93
91
  alias_method :add_light, :<<
94
92
 
@@ -97,7 +95,7 @@ module Hue
97
95
  body = translate_keys(attributes, GROUP_KEYS_MAP)
98
96
 
99
97
  uri = URI.parse(base_url)
100
- http = Net::HTTP.new(uri.host)
98
+ http = HttpClient.create(uri)
101
99
  response = http.request_put(uri.path, JSON.dump(body))
102
100
  JSON(response.body)
103
101
  end
@@ -107,37 +105,39 @@ module Hue
107
105
  body = translate_keys(attributes, STATE_KEYS_MAP)
108
106
 
109
107
  uri = URI.parse("#{base_url}/action")
110
- http = Net::HTTP.new(uri.host)
108
+ http = HttpClient.create(uri)
111
109
  response = http.request_put(uri.path, JSON.dump(body))
112
110
  JSON(response.body)
113
111
  end
114
112
 
115
113
  def refresh
116
- json = JSON(Net::HTTP.get(URI.parse(base_url)))
114
+ uri = URI.parse(base_url)
115
+ http = HttpClient.create(uri)
116
+ json = JSON(http.get(uri.path).body)
117
117
  unpack(json)
118
118
  @lights = nil
119
119
  end
120
120
 
121
121
  def create!
122
122
  body = {
123
- :name => @name,
124
- :lights => @light_ids,
123
+ name: @name,
124
+ lights: @light_ids
125
125
  }
126
126
 
127
- uri = URI.parse("http://#{@bridge.ip}/api/#{@client.username}/groups")
128
- http = Net::HTTP.new(uri.host)
127
+ uri = URI.parse("https://#{@bridge.ip}/api/#{@client.username}/groups")
128
+ http = HttpClient.create(uri)
129
129
  response = http.request_post(uri.path, JSON.dump(body))
130
130
  json = JSON(response.body)
131
131
 
132
- @id = json[0]['success']['id']
132
+ @id = json[0]["success"]["id"]
133
133
  end
134
134
 
135
135
  def destroy!
136
136
  uri = URI.parse(base_url)
137
- http = Net::HTTP.new(uri.host)
137
+ http = HttpClient.create(uri)
138
138
  response = http.delete(uri.path)
139
139
  json = JSON(response.body)
140
- @id = nil if json[0]['success']
140
+ @id = nil if json[0]["success"]
141
141
  end
142
142
 
143
143
  def new?
@@ -147,22 +147,22 @@ module Hue
147
147
  private
148
148
 
149
149
  GROUP_KEYS_MAP = {
150
- :name => :name,
151
- :light_ids => :lights,
152
- :type => :type,
153
- :state => :action
150
+ name: :name,
151
+ light_ids: :lights,
152
+ type: :type,
153
+ state: :action
154
154
  }
155
155
 
156
156
  STATE_KEYS_MAP = {
157
- :on => :on,
158
- :brightness => :bri,
159
- :hue => :hue,
160
- :saturation => :sat,
161
- :xy => :xy,
162
- :color_temperature => :ct,
163
- :alert => :alert,
164
- :effect => :effect,
165
- :color_mode => :colormode,
157
+ on: :on,
158
+ brightness: :bri,
159
+ hue: :hue,
160
+ saturation: :sat,
161
+ xy: :xy,
162
+ color_temperature: :ct,
163
+ alert: :alert,
164
+ effect: :effect,
165
+ color_mode: :colormode
166
166
  }
167
167
 
168
168
  def unpack(data)
@@ -170,12 +170,12 @@ module Hue
170
170
 
171
171
  unless new?
172
172
  unpack_hash(@state, STATE_KEYS_MAP)
173
- @x, @y = @state['xy']
173
+ @x, @y = @state["xy"]
174
174
  end
175
175
  end
176
176
 
177
177
  def base_url
178
- "http://#{@bridge.ip}/api/#{@client.username}/groups/#{id}"
178
+ "https://#{@bridge.ip}/api/#{@client.username}/groups/#{id}"
179
179
  end
180
180
  end
181
181
  end
@@ -0,0 +1,12 @@
1
+ # Helper module for creating HTTPS connections to the Hue Bridge.
2
+ # The Hue Bridge uses a self-signed certificate, so we disable verification.
3
+ module Hue
4
+ module HttpClient
5
+ def self.create(uri)
6
+ http = Net::HTTP.new(uri.host, uri.port)
7
+ http.use_ssl = true
8
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
9
+ http
10
+ end
11
+ end
12
+ end
data/lib/hue/light.rb CHANGED
@@ -3,9 +3,6 @@ module Hue
3
3
  include TranslateKeys
4
4
  include EditableState
5
5
 
6
- HUE_RANGE = 0..65535
7
- SATURATION_RANGE = 0..255
8
- BRIGHTNESS_RANGE = 0..255
9
6
  COLOR_TEMPERATURE_RANGE = 153..500
10
7
 
11
8
  # Unique identification number.
@@ -15,19 +12,19 @@ module Hue
15
12
  attr_reader :bridge
16
13
 
17
14
  # A unique, editable name given to the light.
18
- attr_accessor :name
15
+ attr_reader :name
19
16
 
20
17
  # Hue of the light. This is a wrapping value between 0 and 65535.
21
18
  # Both 0 and 65535 are red, 25500 is green and 46920 is blue.
22
19
  attr_reader :hue
23
20
 
24
- # Saturation of the light. 255 is the most saturated (colored)
21
+ # Saturation of the light. 254 is the most saturated (colored)
25
22
  # and 0 is the least saturated (white).
26
23
  attr_reader :saturation
27
24
 
28
25
  # Brightness of the light. This is a scale from the minimum
29
26
  # brightness the light is capable of, 0, to the maximum capable
30
- # brightness, 255. Note a brightness of 0 is not off.
27
+ # brightness, 254. Note a brightness of 0 is not off.
31
28
  attr_reader :brightness
32
29
 
33
30
  # The x coordinate of a color in CIE color space. Between 0 and 1.
@@ -84,6 +81,15 @@ module Hue
84
81
  # Reserved for future functionality.
85
82
  attr_reader :point_symbol
86
83
 
84
+ # The unique ID of the light.
85
+ attr_reader :uid
86
+
87
+ # The hash of capabilities of the light
88
+ attr_reader :capabilities
89
+
90
+ # The config hash
91
+ attr_reader :config
92
+
87
93
  def initialize(client, bridge, id, hash)
88
94
  @client = client
89
95
  @bridge = bridge
@@ -92,21 +98,21 @@ module Hue
92
98
  end
93
99
 
94
100
  def name=(new_name)
95
- unless (1..32).include?(new_name.length)
96
- raise InvalidValueForParameter, 'name must be between 1 and 32 characters.'
101
+ unless (1..32).cover?(new_name.length)
102
+ raise InvalidValueForParameter, "name must be between 1 and 32 characters."
97
103
  end
98
104
 
99
105
  body = {
100
- :name => new_name
106
+ name: new_name
101
107
  }
102
108
 
103
109
  uri = URI.parse(base_url)
104
- http = Net::HTTP.new(uri.host)
110
+ http = HttpClient.create(uri)
105
111
  response = http.request_put(uri.path, JSON.dump(body))
106
112
  response = JSON(response.body).first
107
- if response['success']
113
+ if response["success"]
108
114
  @name = new_name
109
- # else
115
+ # else
110
116
  # TODO: Error
111
117
  end
112
118
  end
@@ -115,7 +121,7 @@ module Hue
115
121
  # always returns true, functionality will be added in a future
116
122
  # patch.
117
123
  def reachable?
118
- @state['reachable']
124
+ @state["reachable"]
119
125
  end
120
126
 
121
127
  # @param transition The duration of the transition from the light’s current
@@ -126,52 +132,57 @@ module Hue
126
132
  body = translate_keys(attributes, STATE_KEYS_MAP)
127
133
 
128
134
  # Add transition
129
- body.merge!({:transitiontime => transition}) if transition
135
+ body[:transitiontime] = transition if transition
130
136
 
131
137
  uri = URI.parse("#{base_url}/state")
132
- http = Net::HTTP.new(uri.host)
138
+ http = HttpClient.create(uri)
133
139
  response = http.request_put(uri.path, JSON.dump(body))
134
140
  JSON(response.body)
135
141
  end
136
142
 
137
143
  # Refresh the state of the lamp
138
144
  def refresh
139
- json = JSON(Net::HTTP.get(URI.parse(base_url)))
145
+ uri = URI.parse(base_url)
146
+ http = HttpClient.create(uri)
147
+ json = JSON(http.get(uri.path).body)
140
148
  unpack(json)
141
149
  end
142
150
 
143
- private
151
+ private
144
152
 
145
153
  KEYS_MAP = {
146
- :state => :state,
147
- :type => :type,
148
- :name => :name,
149
- :model => :modelid,
150
- :software_version => :swversion,
151
- :point_symbol => :pointsymbol
154
+ state: :state,
155
+ type: :type,
156
+ name: :name,
157
+ model: :modelid,
158
+ software_version: :swversion,
159
+ point_symbol: :pointsymbol,
160
+ uid: :uniqueid,
161
+ capabilities: :capabilities,
162
+ config: :config
152
163
  }
153
164
 
154
165
  STATE_KEYS_MAP = {
155
- :on => :on,
156
- :brightness => :bri,
157
- :hue => :hue,
158
- :saturation => :sat,
159
- :xy => :xy,
160
- :color_temperature => :ct,
161
- :alert => :alert,
162
- :effect => :effect,
163
- :color_mode => :colormode,
164
- :reachable => :reachable,
166
+ on: :on,
167
+ brightness: :bri,
168
+ hue: :hue,
169
+ saturation: :sat,
170
+ xy: :xy,
171
+ color_temperature: :ct,
172
+ alert: :alert,
173
+ effect: :effect,
174
+ color_mode: :colormode,
175
+ reachable: :reachable
165
176
  }
166
177
 
167
178
  def unpack(hash)
168
179
  unpack_hash(hash, KEYS_MAP)
169
180
  unpack_hash(@state, STATE_KEYS_MAP)
170
- @x, @y = @state['xy']
181
+ @x, @y = @state["xy"]
171
182
  end
172
183
 
173
184
  def base_url
174
- "http://#{@bridge.ip}/api/#{@client.username}/lights/#{id}"
185
+ "https://#{@bridge.ip}/api/#{@client.username}/lights/#{id}"
175
186
  end
176
187
  end
177
188
  end
data/lib/hue/scene.rb CHANGED
@@ -24,19 +24,17 @@ module Hue
24
24
  end
25
25
 
26
26
  def lights
27
- @lights ||= begin
28
- @light_ids.map do |light_id|
29
- @client.light(light_id)
30
- end
27
+ @lights ||= @light_ids.map do |light_id|
28
+ @client.light(light_id)
31
29
  end
32
30
  end
33
31
 
34
32
  private
35
33
 
36
34
  SCENE_KEYS_MAP = {
37
- :name => :name,
38
- :light_ids => :lights,
39
- :active => :active,
35
+ name: :name,
36
+ light_ids: :lights,
37
+ active: :active
40
38
  }
41
39
 
42
40
  def unpack(data)
@@ -44,7 +42,7 @@ module Hue
44
42
  end
45
43
 
46
44
  def base_url
47
- "http://#{@bridge.ip}/api/#{@client.username}/scenes/#{id}"
45
+ "https://#{@bridge.ip}/api/#{@client.username}/scenes/#{id}"
48
46
  end
49
47
  end
50
48
  end
@@ -14,7 +14,7 @@ module Hue
14
14
  map.each do |local_key, remote_key|
15
15
  value = hash[remote_key.to_s]
16
16
  next unless value
17
- instance_variable_set("@#{local_key}", value)
17
+ instance_variable_set(:"@#{local_key}", value)
18
18
  end
19
19
  end
20
20
  end
data/lib/hue/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Hue
2
- VERSION = '0.3.0'
2
+ VERSION = "0.3.2"
3
3
  end
data/lib/hue.rb CHANGED
@@ -1,12 +1,13 @@
1
- require 'hue/version'
2
- require 'hue/errors'
3
- require 'hue/client'
4
- require 'hue/bridge'
5
- require 'hue/editable_state'
6
- require 'hue/translate_keys'
7
- require 'hue/light'
8
- require 'hue/group'
9
- require 'hue/scene'
1
+ require "hue/version"
2
+ require "hue/errors"
3
+ require "hue/http_client"
4
+ require "hue/client"
5
+ require "hue/bridge"
6
+ require "hue/editable_state"
7
+ require "hue/translate_keys"
8
+ require "hue/light"
9
+ require "hue/group"
10
+ require "hue/scene"
10
11
 
11
12
  module Hue
12
13
  USERNAME_RANGE = 10..40
@@ -1,25 +1,26 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class ClientTest < Minitest::Test
4
4
  def before_setup
5
5
  super
6
6
 
7
- stub_request(:get, "https://discovery.meethue.com/").
8
- to_return(:body => '[{"id":"ffa57b3b257200065704","internalipaddress":"192.168.0.1"},{"id":"63c2fc01391276a319f9","internalipaddress":"192.168.0.2"}]')
7
+ stub_request(:get, "https://discovery.meethue.com/")
8
+ .to_return(body: '[{"id":"ffa57b3b257200065704","internalipaddress":"192.168.0.1"},{"id":"63c2fc01391276a319f9","internalipaddress":"192.168.0.2"}]')
9
9
 
10
- stub_request(:get, %r{http://192.168.0.1/api/*}).to_return(:body => '[{"success":true}]')
11
- stub_request(:get, %r{http://192.168.0.2/api/*}).to_return(:body => '[{"success":true}]')
10
+ stub_request(:post, "https://192.168.0.1/api").to_return(body: '[{"success":{"username":"ruby"}}]')
11
+ stub_request(:get, %r{https://192.168.0.1/api/*}).to_return(body: '[{"success":true}]')
12
+ stub_request(:get, %r{https://192.168.0.2/api/*}).to_return(body: '[{"success":true}]')
12
13
  end
13
14
 
14
15
  def test_with_bridge_id
15
16
  client = Hue::Client.new(use_mdns: false)
16
- client.stub :find_bridge_id, '63c2fc01391276a319f9' do
17
- assert_equal '63c2fc01391276a319f9', client.bridge.id
17
+ client.stub :find_bridge_id, "63c2fc01391276a319f9" do
18
+ assert_equal "63c2fc01391276a319f9", client.bridge.id
18
19
  end
19
20
  end
20
21
 
21
22
  def test_without_bridge_id
22
23
  client = Hue::Client.new(use_mdns: false)
23
- assert_equal 'ffa57b3b257200065704', client.bridge.id
24
+ assert_equal "ffa57b3b257200065704", client.bridge.id
24
25
  end
25
26
  end
@@ -1,24 +1,44 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class LightTest < Minitest::Test
4
4
  def before_setup
5
5
  super
6
6
 
7
- stub_request(:get, "https://discovery.meethue.com/").
8
- to_return(:body => '[{"internalipaddress":"localhost"}]')
7
+ stub_request(:get, "https://discovery.meethue.com/")
8
+ .to_return(body: '[{"internalipaddress":"localhost"}]')
9
9
 
10
- stub_request(:get, %r{http://localhost/api/*}).to_return(:body => '[{"success":true}]')
11
- stub_request(:post, 'http://localhost/api').to_return(:body => '[{"success":{"username":"ruby"}}]')
12
- stub_request(:put, %r{http://localhost/api*}).to_return(:body => '[{}]')
10
+ stub_request(:get, %r{https://localhost/api/*}).to_return(body: '[{"success":true}]')
11
+ stub_request(:post, "https://localhost/api").to_return(body: '[{"success":{"username":"ruby"}}]')
12
+ stub_request(:put, %r{https://localhost/api*}).to_return(body: "[{}]")
13
13
  end
14
14
 
15
- %w{on hue saturation brightness color_temperature alert effect}.each do |attribute|
16
- define_method "test_setting_#{attribute}" do
15
+ %w[on hue saturation brightness color_temperature alert effect].each do |attribute|
16
+ define_method :"test_setting_#{attribute}" do
17
17
  client = Hue::Client.new(use_mdns: false)
18
18
  light = Hue::Light.new(client, client.bridge, 0, {"state" => {}})
19
19
 
20
- light.send("#{attribute}=", 24)
21
- assert_requested :put, %r{http://localhost/api/.*/lights/0}
20
+ light.send(:"#{attribute}=", 24)
21
+ assert_requested :put, %r{https://localhost/api/.*/lights/0}
22
22
  end
23
23
  end
24
+
25
+ def test_toggle_while_off
26
+ client = Hue::Client.new(use_mdns: false)
27
+ light = Hue::Light.new(client, client.bridge, 0, {"state" => {}})
28
+ assert_equal false, light.on?
29
+
30
+ light.toggle!
31
+ assert_requested :put, %r{https://localhost/api/.*/lights/0}
32
+ assert_equal true, light.on?
33
+ end
34
+
35
+ def test_toggle_while_on
36
+ client = Hue::Client.new(use_mdns: false)
37
+ light = Hue::Light.new(client, client.bridge, 0, {"state" => {"on" => true}})
38
+ assert_equal true, light.on?
39
+
40
+ light.toggle!
41
+ assert_requested :put, %r{https://localhost/api/.*/lights/0}
42
+ assert_equal false, light.on?
43
+ end
24
44
  end
data/test/test_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
- require 'hue'
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "hue"
3
3
 
4
- require 'minitest'
5
- require 'webmock/minitest'
6
- require 'minitest/autorun'
4
+ require "minitest"
5
+ require "webmock/minitest"
6
+ require "minitest/autorun"