wemote 0.2.0 → 0.2.1
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.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/README.md +4 -5
- data/lib/wemote.rb +9 -0
- data/lib/wemote/client.rb +34 -41
- data/lib/wemote/collection/switch.rb +31 -0
- data/lib/wemote/switch.rb +86 -56
- data/lib/wemote/version.rb +1 -1
- data/lib/wemote/xml.rb +14 -13
- data/spec/spec_helper.rb +0 -3
- data/spec/wemote/switch_spec.rb +0 -17
- metadata +12 -11
- data/.coveralls.yml +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88d7829f2bfa8a7892103c87c14696a2efcafcf0
|
4
|
+
data.tar.gz: 8b7e761fbfde70ab48e0ca55cdd50c388c51a9c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7c758531b285a15f3fa50b0eadf8aae1c56ae746b73909789efa795d08414ca1b5f47f8af8fff56aefb48ac9ccd99c0a8d1844dedc4eb1b53a3e9569f84af12
|
7
|
+
data.tar.gz: a9ca631f0509ef3a4d8cab76a1e229b1507a003ccfb62f0e8261ea6669be7ff4799e89944732bc1698394a19b7e6f0e9c1d3f70ac8c9dbb04eb95505472acb06
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Wemote [](https://travis-ci.org/gisikw/wemote) [](http://badge.fury.io/rb/wemote) [](https://travis-ci.org/gisikw/wemote) [](http://badge.fury.io/rb/wemote) [](https://codeclimate.com/github/gisikw/wemote)
|
2
2
|
|
3
3
|
Wemote is an interface for controlling WeMo light switches (and possibly outlets in the future). Unlike other implementations, it does not rely upon `playful` for upnp discovery, which makes it compatible with alternative Ruby engines, such as JRuby.
|
4
4
|
|
@@ -21,18 +21,17 @@ Or install it yourself as:
|
|
21
21
|
You can fetch all lightswitches on your network (providing you've set them up with your smartphone), via:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
switches = Wemote::Switch.all
|
24
|
+
switches = Wemote::Switch.all #=> [#<Wemote::Switch:0x27f33aef @host="192.168.1.11", @name="Kitchen Switch", @port="49154">
|
25
25
|
```
|
26
26
|
|
27
27
|
Or select a switch by its friendly name:
|
28
28
|
|
29
29
|
```ruby
|
30
|
-
switch = Wemote::Switch.find('Kitchen Switch') #=> #<
|
30
|
+
switch = Wemote::Switch.find('Kitchen Switch') #=> #<Wemote::Switch:0x27f33aef @host="192.168.1.11", @name="Kitchen Switch", @port="49154">
|
31
31
|
```
|
32
32
|
|
33
33
|
Given a Switch instance, you can call the following methods:
|
34
34
|
```ruby
|
35
|
-
switch.get_state #=> [:off,:on]
|
36
35
|
switch.off? #=> [true,false]
|
37
36
|
switch.on? #=> [true,false]
|
38
37
|
switch.on!
|
@@ -42,7 +41,7 @@ switch.toggle!
|
|
42
41
|
|
43
42
|
## Performance
|
44
43
|
|
45
|
-
Wemote is designed to be performant - and as such, it will leverage the best HTTP library available for making requests. Currently, Wemote will use (in order of preference): `manticore`, `typhoeus`,
|
44
|
+
Wemote is designed to be performant - and as such, it will leverage the best HTTP library available for making requests. Currently, Wemote will use (in order of preference): `manticore`, `typhoeus`, and finally (miserably) `net/http`. Because you probably like things fast too, we recommend you `gem install manticore` on JRuby, or `gem install typhoeus` on another engine. In order to keep the gem as flexible as possible, none of these are direct dependencies. They just make Wemote happy and fast.
|
46
45
|
|
47
46
|
## Contributing
|
48
47
|
|
data/lib/wemote.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
require_relative './wemote/version'
|
2
2
|
|
3
3
|
module Wemote
|
4
|
+
require_relative './wemote/collection/switch'
|
4
5
|
require_relative './wemote/switch'
|
5
6
|
require_relative './wemote/client'
|
6
7
|
require_relative './wemote/xml'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def discover(broadcast='255.255.255.255')
|
12
|
+
Wemote::Switch.send(:discover,broadcast)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
7
16
|
end
|
data/lib/wemote/client.rb
CHANGED
@@ -6,6 +6,7 @@ module Wemote
|
|
6
6
|
constants.collect {|const_name| const_get(const_name)}.select {|const| const.class == Module}.detect do |mod|
|
7
7
|
fulfilled = false
|
8
8
|
begin
|
9
|
+
next unless mod.const_defined?(:DEPENDENCIES)
|
9
10
|
mod.const_get(:DEPENDENCIES).map{|d|require d}
|
10
11
|
fulfilled = true
|
11
12
|
rescue LoadError
|
@@ -15,63 +16,59 @@ module Wemote
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
module
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
module SmartLib
|
20
|
+
%w{get post put}.each do |name|
|
21
|
+
define_method name do |*args|
|
22
|
+
_req(@lib,name,*args).tap{|r|r.call if @call}
|
23
|
+
end
|
23
24
|
end
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
module Manticore
|
28
|
+
DEPENDENCIES = ['manticore']
|
29
|
+
def self.extended(base)
|
30
|
+
base.instance_variable_set(:@lib,::Manticore)
|
31
|
+
base.instance_variable_set(:@call,true)
|
32
|
+
base.extend(SmartLib)
|
27
33
|
end
|
28
|
-
|
29
34
|
end
|
30
35
|
|
31
36
|
module Typhoeus
|
32
37
|
DEPENDENCIES = ['typhoeus']
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def post(*args)
|
39
|
-
_post(::Typhoeus,*args)
|
38
|
+
def self.extended(base)
|
39
|
+
base.instance_variable_set(:@lib,::Typhoeus)
|
40
|
+
base.extend(SmartLib)
|
40
41
|
end
|
41
|
-
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
DEPENDENCIES = ['httparty']
|
46
|
-
|
47
|
-
def get(*args)
|
48
|
-
_get(::HTTParty,*args)
|
49
|
-
end
|
44
|
+
# HTTParty is temporarily disabled, as it's auto-parsing the XML
|
50
45
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
46
|
+
#module HTTParty
|
47
|
+
# DEPENDENCIES = ['httparty']
|
48
|
+
# def self.extended(base)
|
49
|
+
# base.instance_variable_set(:@lib,::HTTParty)
|
50
|
+
# base.extend(SmartLib)
|
51
|
+
# end
|
52
|
+
#end
|
55
53
|
|
56
54
|
module NetHTTP
|
57
55
|
DEPENDENCIES = ['net/http','uri']
|
58
56
|
|
59
|
-
def
|
57
|
+
def request(klass,url,body=nil,headers=nil)
|
60
58
|
uri = URI.parse(url)
|
61
59
|
http = Net::HTTP.new(uri.host, uri.port)
|
62
|
-
request =
|
60
|
+
request = klass.new(uri.request_uri)
|
63
61
|
headers.map{|k,v|request[k]=v} if headers
|
62
|
+
(request.body = body) if body
|
64
63
|
response = http.request(request)
|
65
64
|
end
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
headers.map{|k,v|request[k]=v} if headers
|
72
|
-
(request.body = body) if body
|
73
|
-
response = http.request(request)
|
66
|
+
%w{get post put}.each do |name|
|
67
|
+
define_method name do |*args|
|
68
|
+
request(Net::HTTP.const_get(name.capitalize),*args)
|
69
|
+
end
|
74
70
|
end
|
71
|
+
|
75
72
|
end
|
76
73
|
|
77
74
|
def initialize
|
@@ -80,12 +77,8 @@ module Wemote
|
|
80
77
|
|
81
78
|
private
|
82
79
|
|
83
|
-
def
|
84
|
-
lib.
|
85
|
-
end
|
86
|
-
|
87
|
-
def _post(lib,url,body=nil,headers=nil)
|
88
|
-
lib.post(url,{body:body,headers:headers})
|
80
|
+
def _req(lib,method,url,body=nil,headers=nil)
|
81
|
+
lib.send(method,url,{body:body,headers:headers})
|
89
82
|
end
|
90
83
|
|
91
84
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Wemote
|
2
|
+
module Collection
|
3
|
+
|
4
|
+
# This class provides an extension on the basic Array object, in order to
|
5
|
+
# facilitate group operations on a collection of Wemote::Switch instances.
|
6
|
+
class Switch < Array
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# @macro [attach] container.increment
|
13
|
+
# @method $1()
|
14
|
+
# Calls {Wemote::Switch#$1} on all containing elements and return the results in an
|
15
|
+
# array
|
16
|
+
# @return [Array]
|
17
|
+
def make(name)
|
18
|
+
define_method(name){map{|s|s.send(name)}}
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
make :toggle!
|
24
|
+
make :off!
|
25
|
+
make :off?
|
26
|
+
make :on!
|
27
|
+
make :on?
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/wemote/switch.rb
CHANGED
@@ -1,19 +1,14 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'ipaddr'
|
3
|
+
require 'timeout'
|
3
4
|
|
4
5
|
module Wemote
|
6
|
+
|
7
|
+
# This class encapsulates an individual Wemo Switch. It provides methods for
|
8
|
+
# getting and setting the switch's state, as well as a {#toggle!} method for
|
9
|
+
# convenience. Finally, it provides the {#poll} method, which accepts a block
|
10
|
+
# to be executed any time the switch changes state.
|
5
11
|
class Switch
|
6
|
-
MULTICAST_ADDR = '239.255.255.250'
|
7
|
-
BIND_ADDR = '0.0.0.0'
|
8
|
-
PORT = 1900
|
9
|
-
DISCOVERY= <<-EOF
|
10
|
-
M-SEARCH * HTTP/1.1\r
|
11
|
-
HOST: 239.255.255.250:1900\r
|
12
|
-
MAN: "ssdp:discover"\r
|
13
|
-
MX: 10\r
|
14
|
-
ST: urn:Belkin:device:lightswitch:1\r
|
15
|
-
\r
|
16
|
-
EOF
|
17
12
|
|
18
13
|
GET_HEADERS = {
|
19
14
|
"SOAPACTION" => '"urn:Belkin:service:basicevent:1#GetBinaryState"',
|
@@ -26,71 +21,95 @@ EOF
|
|
26
21
|
}
|
27
22
|
|
28
23
|
class << self
|
24
|
+
# Returns all Switches detected on the local network
|
25
|
+
#
|
26
|
+
# @param [Boolean] refresh Refresh and redetect Switches
|
27
|
+
# @return [Array] all Switches on the network
|
29
28
|
def all(refresh=false)
|
30
29
|
@switches = nil if refresh
|
31
|
-
@switches ||=
|
30
|
+
@switches ||= Wemote::Collection::Switch.new(discover)
|
32
31
|
end
|
33
32
|
|
33
|
+
# Returns a Switch of a given name
|
34
|
+
#
|
35
|
+
# @param name [String] the friendly name of the Switch
|
36
|
+
# @return [Wemote::Switch] a Switch object
|
34
37
|
def find(name)
|
35
38
|
all.detect{|s|s.name == name}
|
36
39
|
end
|
37
40
|
|
38
41
|
private
|
39
42
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1)
|
45
|
-
socket.bind(BIND_ADDR,PORT)
|
46
|
-
socket
|
47
|
-
end
|
48
|
-
|
49
|
-
def fetch_switches
|
50
|
-
socket = ssdp_socket
|
51
|
-
switches = []
|
52
|
-
|
53
|
-
3.times { socket.send(DISCOVERY, 0, MULTICAST_ADDR, PORT) }
|
54
|
-
|
55
|
-
# The following is a bit silly, but is necessary for JRuby support,
|
56
|
-
# which seems to have some issues with socket interruption.
|
57
|
-
# If you have a working JRuby solution that doesn't require this
|
58
|
-
# kind of hackery, by all means, submit a pull request!
|
59
|
-
|
60
|
-
sleep 1
|
61
|
-
|
62
|
-
parser = Thread.start do
|
63
|
-
loop do
|
64
|
-
message, _ = socket.recvfrom(1024)
|
65
|
-
if message.match(/LOCATION.*Belkin/m)
|
66
|
-
switches << message.match(/LOCATION:\s+http:\/\/([^\/]+)/)[1].split(':')
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
sleep 1
|
72
|
-
parser.kill
|
73
|
-
|
74
|
-
socket.close
|
75
|
-
|
76
|
-
return switches.uniq.map{|s|self.new(*s)}
|
77
|
-
|
43
|
+
def discover(broadcast = '255.255.255.255')
|
44
|
+
`ping -t 1 #{broadcast} > /dev/null && arp -na | grep b4:75`.split("\n").map do |device|
|
45
|
+
self.new(/\((\d+\.\d+\.\d+\.\d+)\)/.match(device)[1])
|
46
|
+
end.reject{|device| device.instance_variable_get(:@port).nil? }
|
78
47
|
end
|
79
48
|
end
|
80
49
|
|
81
50
|
attr_accessor :name
|
82
51
|
|
83
|
-
def initialize(host,port)
|
52
|
+
def initialize(host,port=nil)
|
84
53
|
@host, @port = host, port
|
85
54
|
set_meta
|
86
55
|
end
|
87
56
|
|
57
|
+
# Turn the Switch on or off, based on its current state
|
88
58
|
def toggle!; on? ? off! : on!; end
|
59
|
+
|
60
|
+
# Turn the Switch off
|
89
61
|
def off!; set_state(0); end
|
90
|
-
|
62
|
+
|
63
|
+
# Turn the Switch on
|
91
64
|
def on!; set_state(1); end
|
65
|
+
|
66
|
+
# Return whether the Switch is off
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
def off?; get_state == :off; end
|
70
|
+
|
71
|
+
# Return whether the Switch is on
|
72
|
+
#
|
73
|
+
# @return [Boolean]
|
92
74
|
def on?; get_state == :on; end
|
93
75
|
|
76
|
+
# Monitors the state of the Switch via polling, and yields to the block
|
77
|
+
# given with the updated state.
|
78
|
+
#
|
79
|
+
# @example Output when a Switch changes state
|
80
|
+
# light.poll do |state|
|
81
|
+
# if state == :on
|
82
|
+
# puts "The switch turned on"
|
83
|
+
# else
|
84
|
+
# puts "The switch turned off"
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# @param rate [Float] The rate in seconds at which to poll the switch
|
89
|
+
# @param async [Boolean] Whether or not to poll the switch in a separate thread
|
90
|
+
#
|
91
|
+
# @return [Thread] if the method call was asynchronous
|
92
|
+
def poll(rate=0.25,async=true,&block)
|
93
|
+
old_state = get_state
|
94
|
+
poller = Thread.start do
|
95
|
+
loop do
|
96
|
+
begin
|
97
|
+
state = get_state
|
98
|
+
if state != old_state
|
99
|
+
old_state = state
|
100
|
+
yield state
|
101
|
+
end
|
102
|
+
rescue Exception
|
103
|
+
end
|
104
|
+
sleep rate
|
105
|
+
end
|
106
|
+
end
|
107
|
+
puts "Monitoring #{@name} for changes"
|
108
|
+
async ? poller : poller.join
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
94
113
|
def get_state
|
95
114
|
response = begin
|
96
115
|
client.post("http://#{@host}:#{@port}/upnp/control/basicevent1",Wemote::XML.get_binary_state,GET_HEADERS)
|
@@ -108,15 +127,26 @@ EOF
|
|
108
127
|
end
|
109
128
|
end
|
110
129
|
|
111
|
-
private
|
112
|
-
|
113
130
|
def client
|
114
131
|
@client ||= Wemote::Client.new
|
115
132
|
end
|
116
133
|
|
117
134
|
def set_meta
|
118
|
-
|
119
|
-
|
135
|
+
if @port
|
136
|
+
response = client.get("http://#{@host}:#{@port}/setup.xml")
|
137
|
+
@name = response.body.match(/<friendlyName>([^<]+)<\/friendlyName>/)[1]
|
138
|
+
else
|
139
|
+
for port in 49152..49156
|
140
|
+
begin
|
141
|
+
response = nil
|
142
|
+
Timeout::timeout(1){ response = client.get("http://#{@host}:#{port}/setup.xml") }
|
143
|
+
@name = response.body.match(/<friendlyName>([^<]+)<\/friendlyName>/)[1]
|
144
|
+
@port = port
|
145
|
+
break
|
146
|
+
rescue Exception
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
120
150
|
end
|
121
151
|
|
122
152
|
end
|
data/lib/wemote/version.rb
CHANGED
data/lib/wemote/xml.rb
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
module Wemote
|
2
2
|
module XML
|
3
3
|
class << self
|
4
|
-
|
4
|
+
xml_path = File.join(File.dirname(__FILE__),'/../../xml')
|
5
|
+
TEMPLATES = {
|
6
|
+
get_binary_state: File.read(File.join(xml_path,'get_binary_state.xml')),
|
7
|
+
set_binary_state: File.read(File.join(xml_path,'set_binary_state.xml'))
|
8
|
+
}
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
TEMPLATES[
|
9
|
-
|
10
|
-
define_method basename do |*args|
|
11
|
-
t = TEMPLATES[basename].dup
|
12
|
-
if (replace = t.scan(/{{\d+}}/).size) != args.size
|
13
|
-
raise ArgumentError, "wrong number of arguments calling `#{basename}` (#{args.size} for #{replace})"
|
14
|
-
end
|
15
|
-
(1..replace).map{|i|t.gsub!("{{#{i}}}",args[i-1].to_s)}
|
16
|
-
t
|
17
|
-
end
|
10
|
+
# @return [String] The required XML body for a Wemo binary state request
|
11
|
+
def get_binary_state
|
12
|
+
TEMPLATES[:get_binary_state]
|
13
|
+
end
|
18
14
|
|
15
|
+
# @param [Integer] state Either 1 or 0, for off and on respectively
|
16
|
+
# @return [String] The required XML body for a Wemo binary state set request
|
17
|
+
def set_binary_state(state)
|
18
|
+
TEMPLATES[:set_binary_state].gsub("{{1}}",state.to_s)
|
19
19
|
end
|
20
|
+
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/wemote/switch_spec.rb
CHANGED
@@ -72,22 +72,5 @@ describe Wemote::Switch do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
describe 'get_state' do
|
76
|
-
it 'should return the binary state of the switch' do
|
77
|
-
|
78
|
-
@switch.client.stub(:post).and_return(double('response',body:'<BinaryState>1</BinaryState>'))
|
79
|
-
@switch.get_state.should == :on
|
80
|
-
@switch.client.stub(:post).and_return(double('response',body:'<BinaryState>0</BinaryState>'))
|
81
|
-
@switch.get_state.should == :off
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
describe 'set_state' do
|
86
|
-
it 'should set the binary state of the switch' do
|
87
|
-
@switch.client.stub(:post).and_return(double('response',body:'Called'))
|
88
|
-
@switch.set_state(1).body.should == 'Called'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
75
|
end
|
93
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wemote
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gisi
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Wemote is a Ruby-agnostic gem for Wemo light switches
|
14
14
|
email:
|
@@ -17,15 +17,15 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
-
- .
|
21
|
-
- .
|
22
|
-
- .travis.yml
|
20
|
+
- ".gitignore"
|
21
|
+
- ".travis.yml"
|
23
22
|
- Gemfile
|
24
23
|
- LICENSE.txt
|
25
24
|
- README.md
|
26
25
|
- Rakefile
|
27
26
|
- lib/wemote.rb
|
28
27
|
- lib/wemote/client.rb
|
28
|
+
- lib/wemote/collection/switch.rb
|
29
29
|
- lib/wemote/switch.rb
|
30
30
|
- lib/wemote/version.rb
|
31
31
|
- lib/wemote/xml.rb
|
@@ -38,26 +38,27 @@ homepage: https://github.com/gisikw/wemote
|
|
38
38
|
licenses:
|
39
39
|
- MIT
|
40
40
|
metadata: {}
|
41
|
-
post_install_message:
|
41
|
+
post_install_message:
|
42
42
|
rdoc_options: []
|
43
43
|
require_paths:
|
44
44
|
- lib
|
45
45
|
required_ruby_version: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- -
|
47
|
+
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '0'
|
50
50
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
requirements: []
|
56
|
-
rubyforge_project:
|
56
|
+
rubyforge_project:
|
57
57
|
rubygems_version: 2.2.2
|
58
|
-
signing_key:
|
58
|
+
signing_key:
|
59
59
|
specification_version: 4
|
60
60
|
summary: Wemote is a Ruby-agnostic gem for Wemo light switches
|
61
61
|
test_files:
|
62
62
|
- spec/spec_helper.rb
|
63
63
|
- spec/wemote/switch_spec.rb
|
64
|
+
has_rdoc:
|
data/.coveralls.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
service_name: travis-ci
|