wemote 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/gisikw/wemote.png)](https://travis-ci.org/gisikw/wemote) [![Gem Version](https://badge.fury.io/rb/wemote.png)](http://badge.fury.io/rb/wemote) [![
|
1
|
+
# Wemote [![Build Status](https://travis-ci.org/gisikw/wemote.png)](https://travis-ci.org/gisikw/wemote) [![Gem Version](https://badge.fury.io/rb/wemote.png)](http://badge.fury.io/rb/wemote) [![Code Climate](https://codeclimate.com/github/gisikw/wemote.png)](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
|