hue-lib 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }