hue-lib 0.5.0

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/config.rb ADDED
@@ -0,0 +1,94 @@
1
+ module Hue
2
+ class Config
3
+ STRING_DEFAULT = 'default'
4
+ STRING_BASE_URI = 'base_uri'
5
+ STRING_IDENTIFIER = 'identifier'
6
+
7
+ require 'yaml'
8
+ require 'fileutils'
9
+
10
+ def self.bridges_config_path
11
+ File.join(ENV['HOME'], ".#{APP_NAME}", 'bridges.yml')
12
+ end
13
+
14
+ def self.default
15
+ named(STRING_DEFAULT)
16
+ end
17
+
18
+ def self.named(name)
19
+ yaml = read_file
20
+ if named_yaml = yaml[name]
21
+ Config.new(named_yaml[STRING_BASE_URI], named_yaml[STRING_IDENTIFIER], name)
22
+ else
23
+ raise Error.new("Config named '#{name}' not found.")
24
+ end
25
+ end
26
+
27
+ public
28
+
29
+ attr_reader :base_uri, :identifier, :name
30
+
31
+ def initialize(base_uri, identifier, name = STRING_DEFAULT)
32
+ @base_uri = base_uri
33
+ @identifier = identifier
34
+ @name = name
35
+ end
36
+
37
+ def write(config_file = self.class.bridges_config_path)
38
+ yaml = YAML.load_file(self.class.bridges_config_path) rescue Hash::New
39
+ if yaml.key?(name)
40
+ raise "Configuration named '#{name}' already exists in #{config_file}\nPlease de-register before creating a new one with the same name."
41
+ else
42
+ yaml[name] = {
43
+ STRING_BASE_URI => self.base_uri,
44
+ STRING_IDENTIFIER => identifier.force_encoding('ASCII') # Avoid binary encoded YAML
45
+ }
46
+ self.class.setup_config_path(config_file)
47
+ File.open(config_file, 'w+' ) do |out|
48
+ YAML.dump(yaml, out)
49
+ end
50
+ end
51
+ end
52
+
53
+ def delete
54
+ config_file = self.class.bridges_config_path
55
+ yaml = YAML.load_file(config_file) rescue Hash::New
56
+
57
+ if yaml.key?(name)
58
+ yaml.delete(name)
59
+ end
60
+
61
+ if yaml.size > 0
62
+ self.class.setup_config_path(config_file)
63
+ File.open(config_file, 'w+' ) do |out|
64
+ YAML.dump(yaml, out)
65
+ end
66
+ end
67
+ end
68
+
69
+ def ==(rhs)
70
+ lhs = self
71
+
72
+ lhs.class == rhs.class &&
73
+ lhs.name == rhs.name &&
74
+ lhs.base_uri == rhs.base_uri &&
75
+ lhs.identifier == rhs.identifier
76
+ end
77
+
78
+ private
79
+
80
+ def self.setup_config_path(path)
81
+ dir = File.dirname(path)
82
+ FileUtils.mkdir_p(dir) unless Dir.exists?(dir)
83
+ end
84
+
85
+ def self.read_file(config_file = bridges_config_path)
86
+ begin
87
+ yaml = YAML.load_file(config_file)
88
+ rescue => err
89
+ raise Error.new("Failed to read configuration file", err)
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ default:
3
+ base_uri: http://localhost/api
4
+ identifier: test_identifier
@@ -0,0 +1,75 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Hue::Bridge do
4
+
5
+ def self.klass
6
+ Hue::Bridge
7
+ end
8
+
9
+ def klass
10
+ self.class.klass
11
+ end
12
+
13
+ # it 'should acts as a singleton and give access to the instance' do
14
+ # klass.instance.should be_a_kind_of(Hue::Bridge)
15
+ # end
16
+
17
+ it 'should allow registering a new bridge' do
18
+ pending
19
+ end
20
+
21
+ it 'should allow un-registering a bridge' do
22
+ pending
23
+ end
24
+
25
+ context 'when instantiated with a given config' do
26
+ bridge = klass.new
27
+
28
+ # before(:each) do
29
+ # with_fake_index_request
30
+ # end
31
+
32
+ it 'should report the bridge status' do
33
+ with_fake_request_base
34
+ bridge.status.should == api_reply(:base)
35
+ end
36
+
37
+ it 'should report errors' do
38
+ with_fake_request(:lights, :unauthorized)
39
+ lambda do
40
+ bridge.lights
41
+ end.should raise_error(Hue::API::Error, 'unauthorized user')
42
+ end
43
+
44
+ it 'should report the bridge lights' do
45
+ with_fake_request(:lights)
46
+ bridge.lights.should == api_reply(:lights)
47
+ end
48
+
49
+ it 'should report a simple string of light names' do
50
+ with_fake_request(:lights)
51
+ bridge.light_names.should == "1. Dining\n2. Bedroom Far\n3. Bedroom Near"
52
+ end
53
+
54
+ it 'should report the bridge config' do
55
+ with_fake_request(:config)
56
+ bridge.config.should == api_reply(:config)
57
+ end
58
+
59
+ it 'should report the light schedules' do
60
+ with_fake_request(:schedules)
61
+ bridge.schedules.should == api_reply(:schedules)
62
+ end
63
+
64
+ it 'should return instance of all the bulbs' do
65
+ with_fake_request(:lights)
66
+ bulbs = bridge.bulbs
67
+ bulbs.size.should == 3
68
+ bulbs.each do |bulb|
69
+ bulb.should be_a(Hue::Bulb)
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Hue::Bulb do
4
+
5
+ def self.klass
6
+ Hue::Bulb
7
+ end
8
+
9
+ def klass
10
+ self.class.klass
11
+ end
12
+
13
+ context 'when instantiated with a given bridge and id' do
14
+ bulb = klass.new(Hue::Bridge.new, 1)
15
+
16
+ before(:all) do
17
+ with_fake_request('lights/1')
18
+ @test_status = api_reply('lights/1')
19
+ end
20
+
21
+ it 'should report the bulb state' do
22
+ bulb.state.should == @test_status['state']
23
+ end
24
+
25
+ it 'should report the bulb info' do
26
+ info = api_reply('lights/1')
27
+ info.delete('state')
28
+ info.delete('pointsymbol')
29
+ bulb.info.should == info
30
+ end
31
+
32
+ it "should report it's name" do
33
+ bulb.name.should == @test_status['name']
34
+ end
35
+
36
+ it "should report if it's on" do
37
+ bulb.on?.should be_false
38
+ end
39
+
40
+ it "should report if it's off" do
41
+ bulb.off?.should be_true
42
+ end
43
+
44
+ it "should report the hue, brightness and saturation" do
45
+ bulb.hue.should == 13234
46
+ bulb.brightness.should == 146
47
+ bulb.bri.should == 146
48
+ bulb.saturation.should == 208
49
+ bulb.sat.should == bulb.saturation
50
+ end
51
+
52
+ it "should report the color temperature and color mode" do
53
+ bulb.color_temperature.should == 459
54
+ bulb.ct.should == bulb.color_temperature
55
+ bulb.color_mode.should == 'ct'
56
+ bulb.color_mode.should == bulb.colormode
57
+ end
58
+
59
+ it "should report the alert state" do
60
+ bulb.blinking?.should be_false
61
+ bulb.solid?.should be_true
62
+ end
63
+
64
+ context 'by changing state' do
65
+
66
+ it 'should allow turning bulps on and off' do
67
+ with_fake_update('lights/1/state', on: true)
68
+ bulb.on.should be_true
69
+
70
+ with_fake_update('lights/1/state', on: false)
71
+ bulb.off.should be_true
72
+ end
73
+
74
+ it 'should allow setting hue, saturation and brightness' do
75
+ with_fake_update('lights/1/state', hue: 21845)
76
+ bulb.hue = 120
77
+ bulb.hue.should == 21845
78
+
79
+ with_fake_update('lights/1/state', sat: 1293)
80
+ bulb.saturation = 1293
81
+ bulb.saturation.should == 1293
82
+
83
+ with_fake_update('lights/1/state', bri: 233)
84
+ bulb.brightness = 233
85
+ bulb.brightness.should == 233
86
+ end
87
+
88
+ it 'should allow setting blink, solid and flash alerts' do
89
+ with_fake_update('lights/1/state', alert: 'lselect')
90
+ bulb.blink
91
+ bulb.blinking?.should be_true
92
+
93
+ with_fake_update('lights/1/state', alert: 'none')
94
+ bulb.solid
95
+ bulb.solid?.should be_true
96
+
97
+ with_fake_update('lights/1/state', alert: 'select') do
98
+ with_fake_update('lights/1/state', alert: 'none')
99
+ bulb.flash
100
+ bulb.solid?.should be_true
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Hue::Config do
4
+
5
+ TEST_IDENTIFIER = 'test_identifier'
6
+
7
+ def self.klass
8
+ Hue::Config
9
+ end
10
+
11
+ def klass
12
+ self.class.klass
13
+ end
14
+
15
+ after(:all) do
16
+ File.open(TEST_BRIDGE_CONFIG_PATH, 'w' ) do |out|
17
+ YAML.dump(TEST_BRIDGE_CONFIG, out)
18
+ end
19
+ end
20
+
21
+ it 'should report the bridge config file location' do
22
+ klass.bridges_config_path.should == TEST_BRIDGE_CONFIG_PATH
23
+ end
24
+
25
+ it "should throw and error if a named config doesn't exist" do
26
+ lambda do
27
+ klass.named('not_default')
28
+ end.should raise_error(Hue::Error, /Config named (.*) not found/)
29
+ end
30
+
31
+ context 'with a bridge config file, containing the default bridge' do
32
+ it "should give the default config and report it's values" do
33
+ config = klass.default
34
+ config.name == klass::STRING_DEFAULT
35
+ config.base_uri == TEST_BRIDGE_CONFIG[config.name][klass::STRING_BASE_URI]
36
+ config.identifier == TEST_BRIDGE_CONFIG[config.name][klass::STRING_IDENTIFIER]
37
+ end
38
+ end
39
+
40
+ context 'given an new config' do
41
+ config = klass.new('http://someip/api', 'some_id', 'not_default')
42
+
43
+ it 'should report the values' do
44
+ config.name == 'not_default'
45
+ config.base_uri == 'http://someip/api'
46
+ config.identifier == 'not_default'
47
+ end
48
+
49
+ it 'should allow writing the new config to file' do
50
+ config.write
51
+ YAML.load_file(klass.bridges_config_path)['not_default'].should be_a(Hash)
52
+ end
53
+
54
+ it 'should allow fetching that name config' do
55
+ named_config = klass.named('not_default')
56
+ named_config.should == config
57
+ end
58
+
59
+ it 'should allow deleting that named config from the file' do
60
+ config.delete
61
+ YAML.load_file(klass.bridges_config_path)['not_default'].should be_nil
62
+ end
63
+ end
64
+
65
+ end
data/spec/hue_spec.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Hue do
4
+
5
+ it 'should report the device type as itself' do
6
+ Hue.device_type.should == Hue::DEVICE_TYPE
7
+ end
8
+
9
+ it 'should return the default config' do
10
+ Hue.config.should be_a(Hue::Config)
11
+ end
12
+
13
+ end
@@ -0,0 +1,117 @@
1
+ {
2
+ "lights" : {
3
+ "1" : {
4
+ "state" : {
5
+ "on" : false,
6
+ "bri" : 146,
7
+ "hue" : 13234,
8
+ "sat" : 208,
9
+ "xy" : [ "0.5090", "0.4149" ],
10
+ "ct" : 459,
11
+ "alert" : "none",
12
+ "effect" : "none",
13
+ "colormode" : "ct",
14
+ "reachable" : true
15
+ },
16
+ "type" : "Extended color light",
17
+ "name" : "Living",
18
+ "modelid" : "LCT001",
19
+ "swversion" : "65003148",
20
+ "pointsymbol" : {
21
+ "1" : "none",
22
+ "2" : "none",
23
+ "3" : "none",
24
+ "4" : "none",
25
+ "5" : "none",
26
+ "6" : "none",
27
+ "7" : "none",
28
+ "8" : "none"
29
+ }
30
+ },
31
+ "2" : {
32
+ "state" : {
33
+ "on" : false,
34
+ "bri" : 162,
35
+ "hue" : 2213,
36
+ "sat" : 238,
37
+ "xy" : [ "0.6349", "0.3413" ],
38
+ "ct" : 500,
39
+ "alert" : "none",
40
+ "effect" : "none",
41
+ "colormode" : "xy",
42
+ "reachable" : true
43
+ },
44
+ "type" : "Extended color light",
45
+ "name" : "Bedroom Far",
46
+ "modelid" : "LCT001",
47
+ "swversion" : "65003148",
48
+ "pointsymbol" : {
49
+ "1" : "none",
50
+ "2" : "none",
51
+ "3" : "none",
52
+ "4" : "none",
53
+ "5" : "none",
54
+ "6" : "none",
55
+ "7" : "none",
56
+ "8" : "none"
57
+ }
58
+ },
59
+ "3" : {
60
+ "state" : {
61
+ "on" : false,
62
+ "bri" : 146,
63
+ "hue" : 13122,
64
+ "sat" : 211,
65
+ "xy" : [ "0.3565", "0.1775" ],
66
+ "ct" : 462,
67
+ "alert" : "none",
68
+ "effect" : "none",
69
+ "colormode" : "ct",
70
+ "reachable" : true
71
+ },
72
+ "type" : "Extended color light",
73
+ "name" : "Bedroom Near",
74
+ "modelid" : "LCT001",
75
+ "swversion" : "65003148",
76
+ "pointsymbol" : {
77
+ "1" : "none",
78
+ "2" : "none",
79
+ "3" : "none",
80
+ "4" : "none",
81
+ "5" : "none",
82
+ "6" : "none",
83
+ "7" : "none",
84
+ "8" : "none"
85
+ }
86
+ }
87
+ },
88
+ "groups" : { },
89
+ "config" : {
90
+ "name" : "Philips hue",
91
+ "mac" : "00:17:88:09:26:9d",
92
+ "dhcp" : true,
93
+ "ipaddress" : "10.255.255.44",
94
+ "netmask" : "255.255.255.0",
95
+ "gateway" : "10.255.255.254",
96
+ "proxyaddress" : "",
97
+ "proxyport" : 0,
98
+ "UTC" : "2012-11-02T18:34:38",
99
+ "whitelist" : {
100
+ "0123456789abdcef0123456789abcdef" : {
101
+ "last use date" : "2012-11-02T18:34:38",
102
+ "create date" : "2012-11-01T21:04:48",
103
+ "name" : "iPhone 5"
104
+ }
105
+ },
106
+ "swversion" : "01003542",
107
+ "swupdate" : {
108
+ "updatestate" : 0,
109
+ "url" : "",
110
+ "text" : "",
111
+ "notify" : false
112
+ },
113
+ "linkbutton" : false,
114
+ "portalservices" : false
115
+ },
116
+ "schedules" : { }
117
+ }