hue-lib 0.7.1 → 0.7.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/Rakefile CHANGED
@@ -16,4 +16,30 @@ rescue LoadError
16
16
  end
17
17
  end
18
18
 
19
+ namespace :gem do
20
+ desc 'Build the gem'
21
+ task :build do
22
+ `mkdir -p pkg`
23
+ `gem build hue-lib.gemspec`
24
+ `mv *.gem pkg/`
25
+ end
26
+
27
+ desc 'Publish the gem'
28
+ task :publish do
29
+ gem = `ls pkg`
30
+ # `gem push pkg/#{gem}`
31
+ end
32
+
33
+ desc 'Install the gem locally'
34
+ task :install do
35
+ gem = `ls pkg`.split.sort
36
+ `gem install pkg/#{gem.last}`
37
+ end
38
+ end
39
+
40
+ desc 'Remove generated files'
41
+ task :clean do
42
+ `rm -rf pkg`
43
+ end
44
+
19
45
  task :default => [:spec]
data/hue-lib.gemspec CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "hue-lib"
6
- s.version = '0.7.1'
6
+ s.version = '0.7.2'
7
7
  s.authors = ["Birkir A. Barkarson", "Aaron Hurley"]
8
8
  s.email = ["birkirb@stoicviking.net"]
9
9
  s.homepage = "https://github.com/birkirb/hue-lib"
data/lib/hue/bridge.rb CHANGED
@@ -104,7 +104,7 @@ module Hue
104
104
  begin
105
105
  http = Net::HTTP.new(url.host, url.port)
106
106
  http.open_timeout = 3 # Quick timeout on connection fail.
107
- http.read_timeout = 10 # Slower timeout on read fail, but way faster than the default.
107
+ http.read_timeout = 8 # Slower timeout on read fail, but way faster than the default.
108
108
  response = http.start { |http| http.request(request) }
109
109
  rescue => err
110
110
  Hue.logger.error(err.message)
data/lib/hue/bulb.rb CHANGED
@@ -5,6 +5,8 @@ module Hue
5
5
  class Bulb
6
6
 
7
7
  BRIGHTNESS_MAX = 255
8
+ NONE = 'none'
9
+ COLOR_LOOP = 'colorloop'
8
10
 
9
11
  include Animations::Candle
10
12
  include Animations::Sunrise
@@ -34,6 +36,11 @@ module Hue
34
36
  status['state']
35
37
  end
36
38
 
39
+ # Free for all, no checking.
40
+ def state=(value)
41
+ update_state(value)
42
+ end
43
+
37
44
  def [](item)
38
45
  state[item.to_s]
39
46
  end
@@ -104,36 +111,68 @@ module Hue
104
111
  set_color
105
112
  end
106
113
 
114
+ def effect
115
+ self['effect']
116
+ end
117
+
118
+ def effect=(value)
119
+ update_state(effect: value.to_s)
120
+ end
121
+
122
+ def effect?
123
+ NONE != self.effect
124
+ end
125
+
126
+ def color_loop
127
+ self.effect = COLOR_LOOP
128
+ end
129
+ alias :colorloop :color_loop
130
+
131
+ def color_loop?
132
+ COLOR_LOOP == self.effect
133
+ end
134
+
135
+ def clear_effect
136
+ self.effect = NONE
137
+ end
138
+
139
+ def alert
140
+ self['alert']
141
+ end
142
+
143
+ def alert=(value)
144
+ update_state(alert: value.to_s)
145
+ end
146
+
107
147
  def blinking?
108
148
  !solid?
109
149
  end
110
150
 
111
151
  def solid?
112
- 'none' == self['alert']
152
+ NONE == alert
113
153
  end
114
154
 
115
155
  def blink
116
- update_state(alert: 'lselect')
156
+ self.alert = 'lselect'
117
157
  end
118
158
 
119
159
  def solid
120
- update_state(alert: 'none')
160
+ self.alert = NONE
121
161
  end
122
162
 
123
163
  def flash
124
- update_state(alert: 'select')
164
+ self.alert = 'select'
125
165
  # immediately update to expected state
126
- @status['state']['alert'] = 'none'
166
+ @status['state']['alert'] = NONE
127
167
  end
128
168
 
169
+ # transition time in seconds
129
170
  def transition_time
130
- # transition time in seconds
131
- (options[:transitiontime] || 1).to_f / 10
171
+ (options[:transitiontime] || 0).to_f / 10
132
172
  end
133
173
 
134
174
  def transition_time=(time)
135
- # transition time in seconds
136
- self.options[:transitiontime] = (time * 10).to_i
175
+ self.options[:transitiontime] = (time.to_f * 10).to_i
137
176
  end
138
177
 
139
178
  private
@@ -18,7 +18,9 @@ module Hue
18
18
  end
19
19
 
20
20
  def write(overwrite_existing_key = false)
21
- yaml = YAML.load_file(self.path) rescue Hash.new
21
+ yaml = YAML.load_file(self.path) rescue nil
22
+ yaml ||= Hash.new
23
+
22
24
  if yaml.key?(name) && !overwrite_existing_key
23
25
  raise "Key named '#{name}' already exists in config file '#{self.path}'.\nPlease remove it before creating a new one with the same name."
24
26
  else
@@ -16,7 +16,7 @@ module Hue
16
16
 
17
17
  def self.named(name)
18
18
  yaml = read_file(file_path)
19
- if named_yaml = yaml[name]
19
+ if yaml && named_yaml = yaml[name]
20
20
  new(named_yaml[STRING_BRIDGE_ID], named_yaml[STRING_ID], name)
21
21
  else
22
22
  raise NotFound.new("Config named '#{name}' not found.")
data/lib/hue.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'net/http'
2
- require 'digest/md5'
3
2
  require 'json'
4
- require 'uuid'
3
+ require 'securerandom'
5
4
 
6
5
  module Hue
7
6
 
@@ -15,7 +14,7 @@ module Hue
15
14
  end
16
15
 
17
16
  def self.one_time_uuid
18
- Digest::MD5.hexdigest(UUID.generate)
17
+ SecureRandom.hex(16)
19
18
  end
20
19
 
21
20
  def self.register_default
@@ -71,10 +70,16 @@ ST: ssdp:all
71
70
  loop do
72
71
  message, (address_family, port, hostname, ip_add) = socket.recvfrom(1024)
73
72
  if message =~ /IpBridge/ && location = /LOCATION: (.*)$/.match(message)
74
- if uuid = /uuid:(.{36})/.match(message)
73
+ if uuid_match = /uuid:(.{36})/.match(message)
75
74
  # Assume this is Philips Hue for now.
76
- bridges[uuid.captures.first] = "http://#{ip_add}/api"
75
+ uuid = uuid_match.captures.first
76
+ if bridges[uuid].nil?
77
+ logger.info("Found bridge (#{hostname}:#{port}) with uuid: #{uuid}")
78
+ end
79
+ bridges[uuid] = "http://#{ip_add}/api"
77
80
  end
81
+ else
82
+ logger.debug("Found #{hostname}:#{port}: #{message}")
78
83
  end
79
84
  end
80
85
  end
@@ -0,0 +1 @@
1
+ --- {}
@@ -48,10 +48,21 @@ describe Hue::Bulb do
48
48
  end
49
49
 
50
50
  it "should report the alert state" do
51
+ bulb.alert.should == 'none'
51
52
  bulb.blinking?.should be_false
52
53
  bulb.solid?.should be_true
53
54
  end
54
55
 
56
+ it "should report the effect state" do
57
+ bulb.effect?.should be_false
58
+ bulb.effect.should == 'none'
59
+ bulb.color_loop?.should be_false
60
+ end
61
+
62
+ it "should report the transition time" do
63
+ bulb.transition_time.should == 0
64
+ end
65
+
55
66
  context 'by changing state' do
56
67
 
57
68
  it 'should allow turning bulps on and off' do
@@ -107,6 +118,38 @@ describe Hue::Bulb do
107
118
  bulb.flash
108
119
  bulb.solid?.should be_true
109
120
  end
121
+
122
+ with_fake_update('lights/1/state', alert: 'crap') do
123
+ bulb.alert = 'crap'
124
+ end
125
+ end
126
+
127
+ it 'should allow setting colorloop, and effect' do
128
+ with_fake_update('lights/1/state', effect: 'new')
129
+ bulb.effect = 'new'
130
+ bulb.effect.should == 'new'
131
+ bulb.effect?.should be_true
132
+
133
+ with_fake_update('lights/1/state', effect: 'colorloop')
134
+ bulb.colorloop
135
+ bulb.effect.should == 'colorloop'
136
+ bulb.effect?.should be_true
137
+ bulb.color_loop?.should be_true
138
+
139
+ with_fake_update('lights/1/state', effect: 'none')
140
+ bulb.clear_effect
141
+ bulb.effect.should == 'none'
142
+
143
+ with_fake_update('lights/1/state', effect: 'colorloop')
144
+ bulb.color_loop
145
+ bulb.effect.should == 'colorloop'
146
+ end
147
+
148
+ it 'should allow setting the transitions time, and employ it for a state change' do
149
+ bulb.transition_time = 10
150
+
151
+ with_fake_update('lights/1/state', transitiontime: 100, bri: 255)
152
+ bulb.brightness = 255
110
153
  end
111
154
 
112
155
  end
@@ -32,4 +32,14 @@ describe Hue::Config::Abstract do
32
32
  end
33
33
  end
34
34
 
35
+ context 'given an existing config' do
36
+ config = described_class.new('test', EMPTY_CONFIG_FILE)
37
+
38
+ it 'should allow writing to the file' do
39
+ config.write
40
+ YAML.load_file(config.path)['test'].should be_a(Hash)
41
+ config.delete
42
+ end
43
+ end
44
+
35
45
  end
@@ -58,4 +58,15 @@ describe Hue::Config::Application do
58
58
  end
59
59
  end
60
60
 
61
+ context 'given an non existing config' do
62
+
63
+ it "should throw and error if a named config doesn't exist" do
64
+ described_class.expects(:file_path).returns(EMPTY_CONFIG_FILE).once
65
+
66
+ lambda do
67
+ described_class.named('not_default')
68
+ end.should raise_error(Hue::Config::NotFound, /Config named (.*) not found/)
69
+ end
70
+ end
71
+
61
72
  end
@@ -21,7 +21,7 @@ describe Hue::Config::Bridge do
21
21
  end
22
22
 
23
23
  context 'given an new config' do
24
- uuid = UUID.generate
24
+ uuid = Hue.one_time_uuid
25
25
  uri = 'http://someip/api'
26
26
  config = described_class.new(uuid, uri)
27
27
 
data/spec/spec_helper.rb CHANGED
@@ -36,6 +36,7 @@ def silence_warnings
36
36
  end
37
37
 
38
38
  SPEC_DIR = File.dirname(__FILE__)
39
+ Hue.logger.level = Logger::DEBUG
39
40
 
40
41
  silence_warnings do
41
42
  Hue.const_set(:DEFAULT_UDP_TIMEOUT, 0.01)
@@ -43,6 +44,7 @@ end
43
44
 
44
45
  # APPLICATION CONFIG
45
46
 
47
+ EMPTY_CONFIG_FILE = File.join(SPEC_DIR, 'config', 'empty.yml')
46
48
  TEST_CONFIG_APPLICATION_PATH = File.join(SPEC_DIR, 'config', 'applications.yml')
47
49
  TEST_CONFIG_APPLICATION = YAML.load_file(TEST_CONFIG_APPLICATION_PATH)
48
50
  TEST_JSON_DATA_PATH = File.join(SPEC_DIR, 'json')
@@ -0,0 +1,44 @@
1
+ require 'hue'
2
+
3
+ bridge = Hue.application
4
+
5
+ MAX_LIGHTS = 4
6
+
7
+ @run = true
8
+ @command_count = 0
9
+ @error_count = 0
10
+
11
+ trap("SIGINT") do
12
+ @run = false
13
+ end
14
+
15
+ while(@run)
16
+ begin
17
+ @command_count += 1
18
+ number = rand(4)
19
+ light = rand(MAX_LIGHTS)
20
+ case number
21
+ when 0
22
+ puts bridge.status['config']['UTC']
23
+ when 1
24
+ print "#{light + 1} name: "
25
+ puts bridge.bulbs[light].refresh!['name']
26
+ when 2
27
+ print "#{light + 1} on: "
28
+ puts bridge.bulbs[light].on
29
+ when 3
30
+ print "#{light + 1} off: "
31
+ puts bridge.bulbs[light].off
32
+ else
33
+ raise "Unhandled case: #{number}"
34
+ end
35
+ rescue => err
36
+ puts err.message
37
+ @error_count += 1
38
+ end
39
+ sleep 1
40
+ end
41
+
42
+ puts "Command count: #{@command_count}"
43
+ puts "Fail count: #{@error_count}"
44
+ puts "Success rate: #{(1.0 - (@error_count/@command_count.to_f)) * 100}%"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hue-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-06 00:00:00.000000000 Z
13
+ date: 2013-09-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -106,6 +106,7 @@ files:
106
106
  - lib/hue/config/bridge.rb
107
107
  - spec/config/applications.yml
108
108
  - spec/config/bridges.yml
109
+ - spec/config/empty.yml
109
110
  - spec/hue/bridge_spec.rb
110
111
  - spec/hue/bulb_spec.rb
111
112
  - spec/hue/colors/color_spec.rb
@@ -130,6 +131,7 @@ files:
130
131
  - spec/json/schedules.json
131
132
  - spec/json/unauthorized.json
132
133
  - spec/spec_helper.rb
134
+ - test/connectivity.rb
133
135
  homepage: https://github.com/birkirb/hue-lib
134
136
  licenses: []
135
137
  post_install_message:
@@ -155,4 +157,3 @@ signing_key:
155
157
  specification_version: 3
156
158
  summary: Ruby library for controlling the Philips Hue system's lights and bridge.
157
159
  test_files: []
158
- has_rdoc: