wifly 0.0.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in wifly.gemspec
4
4
  gemspec
5
+
6
+ group :test, :development do
7
+ gem "pry"
8
+ end
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Wifly
2
2
 
3
- This Ruby gem can be used to talk to a [WiFly RN-XV](http://www.rovingnetworks.com/products/RN171XV) device at a specified address. The gem has no dependencies other than the socket libraries that ship with any Ruby installation. The gem takes advantage of the predictable size of the response strings from the WiFly by using blocking IO operations. Using blocking operations significantly reduces the complexity of the code. There are a couple of caveats with using blocking reads, though:
4
- * Unexpected output from the WiFly could cause the client to deadlock on a socket read, or could cause corrupt data in subsequent reads. I've tried to find the variants of output that I'm concerned with in my project; however, you might find some edge cases I haven't covered. In that case, pull requests are welcome.
5
- * The version of the firmware must be passed into the client so that the size of the response strings can be predicted.
3
+ This Ruby gem can be used to talk to a [WiFly RN-XV](http://www.rovingnetworks.com/products/RN171XV) device at a specified address. The gem has no dependencies other than the socket libraries that ship with any Ruby installation.
6
4
 
7
5
  ## Installation
8
6
 
@@ -19,9 +17,9 @@ Or install it yourself as:
19
17
  $ gem install wifly
20
18
 
21
19
  ## Usage
22
- Create a client by passing in the host, port, and firmware version:
20
+ Create a client by passing in the host and port
23
21
 
24
- control = Wifly::Control.new('192.168.1.45', 2000, '2.38.3')
22
+ control = Wifly::Control.new('192.168.1.45', 2000)
25
23
 
26
24
  Read the high pins:
27
25
 
@@ -1,3 +1,4 @@
1
+ $: << File.expand_path("..", __FILE__)
1
2
  require 'socket'
2
3
  require 'wifly/connection'
3
4
  require 'wifly/calculations'
@@ -5,9 +5,9 @@ module Wifly
5
5
  # into an array of pins that are high
6
6
  #
7
7
  def parse_io(hex_str)
8
- #"8d08" 36104 "1000110100001000" make it 16 bits
9
- binary_string = hex_str .hex .to_s(2) .rjust(hex_str.size*4, '0')
10
- binary_string.reverse.split("").each_with_index.map do |value, pin|
8
+ # use sprintf to pad with 0's to 16 bits
9
+ binary_string = "%016b" % hex_str.hex.to_i
10
+ binary_string.reverse.chars.each_with_index.map do |value, pin|
11
11
  pin if value == "1"
12
12
  end.compact
13
13
  end
@@ -1,15 +1,13 @@
1
1
  module Wifly
2
2
  class Connection
3
- attr_accessor :address, :port, :version
3
+ attr_accessor :address, :port
4
4
 
5
5
  ##
6
6
  # address => the hostname or IP address of the wifly device
7
7
  # port => the port for communicating with the wifly
8
- # version => the firmware version of the device
9
- def initialize(address, port, version)
8
+ def initialize(address, port)
10
9
  self.address = address
11
10
  self.port = port
12
- self.version = version
13
11
  end
14
12
 
15
13
  ##
@@ -19,18 +17,15 @@ module Wifly
19
17
  # The wifly will echo back the command (with carriage return)
20
18
  # along with another CRLF and the command prompt string.
21
19
  # Something like "lites\r\r\n<2.32> "
22
- # Since the string has a predictable length, we can do a blocking read.
23
20
  #
24
- def send_command(str, return_len=0)
21
+ def send_command(str)
25
22
  str += "\r"
26
- socket.write(str)
27
- expected_return_length = str.length + "\r\n#{prompt}".length + return_len
28
- socket.read(expected_return_length).gsub(prompt,'')
29
- rescue Errno::EPIPE # connection closed on the client end
30
- initialize_socket
31
- retry
23
+ write(socket, str) # the write is blocking
24
+ sleep(0.2)
25
+ read(socket).gsub(prompt,'')
32
26
  end
33
27
 
28
+
34
29
  def close
35
30
  socket.close
36
31
  end
@@ -40,14 +35,43 @@ module Wifly
40
35
  end
41
36
 
42
37
  private
38
+
39
+ def read(sock)
40
+ result = ''
41
+ begin
42
+ result = sock.read_nonblock(1024)
43
+ # connection lost somehow
44
+ rescue Errno::ECONNRESET, Errno::EPIPE, IOError
45
+ initialize_socket
46
+ read(socket)
47
+ # No more data on socket
48
+ rescue Errno::EAGAIN
49
+ retry
50
+ rescue EOFError => e
51
+ # ain't nothin left on socket.
52
+ end
53
+ result.strip
54
+ end
55
+
56
+ def write(sock, str)
57
+ begin
58
+ sock.write(str)
59
+ rescue Errno::EPIPE, IOError
60
+ initialize_socket
61
+ write(socket, str)
62
+ end
63
+ end
64
+
43
65
  def prompt
44
- "<#{version}> "
66
+ # 2.32
67
+ # 3.2.23
68
+ /<[0-9]{1,}\.[0-9]{1,}(\.[0-9]{1,})?>/
45
69
  end
46
70
 
47
71
  def initialize_socket
48
72
  sock = Socket.tcp(address, port)
49
- sock.write(COMMAND_MODE) # enter command mode
50
- sock.read(HELLO.length) # read off the response
73
+ write(sock, COMMAND_MODE) # enter command mode
74
+ read(sock) # read off the response
51
75
  sock
52
76
  end
53
77
  end
@@ -7,12 +7,11 @@ module Wifly
7
7
  ##
8
8
  # address - the hostname or IP address
9
9
  # port - defaults to 2000
10
- # version - the firmware version (a string)
11
10
  # connection - the object to use for talking to the wifly. Responds to "send_command."
12
11
  # Defaults to Wifly::Connection.
13
12
  #
14
- def initialize(address, port, version, connection=nil)
15
- self.connection = connection || Connection.new(address, port, version)
13
+ def initialize(address, port, connection=nil)
14
+ self.connection = connection || Connection.new(address, port)
16
15
  end
17
16
 
18
17
  ##
@@ -27,7 +26,7 @@ module Wifly
27
26
  #
28
27
  def set_high(pin)
29
28
  hex = pin_to_hex(pin)
30
- connection.send_command "set sys output #{hex} #{hex}", AOK.length
29
+ connection.send_command "set sys output #{hex} #{hex}"
31
30
  end
32
31
 
33
32
  ##
@@ -35,7 +34,7 @@ module Wifly
35
34
  #
36
35
  def set_low(pin)
37
36
  hex = pin_to_hex(pin)
38
- connection.send_command "set sys output 0x00 #{hex}", AOK.length
37
+ connection.send_command "set sys output 0x00 #{hex}"
39
38
  end
40
39
 
41
40
  ##
@@ -52,6 +51,13 @@ module Wifly
52
51
  parse_io(read_io)
53
52
  end
54
53
 
54
+ ##
55
+ # Close the connection.
56
+ #
57
+ def close
58
+ connection.close
59
+ end
60
+
55
61
  private
56
62
 
57
63
  ##
@@ -60,9 +66,8 @@ module Wifly
60
66
  def read_io
61
67
  cmd = "show io\r"
62
68
 
63
- # wifly spits back something like 'show io\r\r\n8d08\r\n<2.32> '
64
- # crucially, the middle part "8d08\r\n" is always the same length
65
- str = connection.send_command("show io", "8d08\r\n".length)
69
+ # result is something like 'show io\r\r\n8d08'
70
+ str = connection.send_command "show io"
66
71
 
67
72
  # Return only the middle part, which is the io state
68
73
  str.gsub(cmd,'').strip
@@ -1,3 +1,3 @@
1
1
  module Wifly
2
- VERSION = "0.0.5"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -15,5 +15,7 @@ describe Wifly::Calculations do
15
15
  control = WiflyControlShim.new
16
16
  result = control.parse_io('8d08')
17
17
  result.should eq([3, 8, 10, 11, 15])
18
+ result = control.parse_io('8103')
19
+ result.should eq([0, 1, 8, 15])
18
20
  end
19
21
  end
@@ -13,12 +13,12 @@ class Wifly::TestServer
13
13
  @client.close
14
14
  end
15
15
 
16
- def receive_command(command, version)
16
+ def receive_command(command)
17
17
  @client = @socket.accept
18
18
 
19
19
  @client.write(Wifly::HELLO)
20
20
  @client.read((command + "\r").length)
21
- @client.write(command + "\r\r\n<" + version + '> ')
21
+ @client.write(command + "\r\r\n<2.32> ")
22
22
  @client.close
23
23
  end
24
24
  end
@@ -28,17 +28,16 @@ describe Wifly::Connection do
28
28
  @server = Wifly::TestServer.new(2000)
29
29
  end
30
30
  it 'should set stuff on initialize' do
31
- connection = Wifly::Connection.new('localhost', 2000, '2.3.13')
31
+ connection = Wifly::Connection.new('localhost', 2000)
32
32
  connection.address.should eq('localhost')
33
33
  connection.port.should eq(2000)
34
- connection.version.should eq('2.3.13')
35
34
  end
36
35
 
37
36
  it 'should enter command mode' do
38
37
  t = Thread.new do
39
38
  @server.simple_connect
40
39
  end
41
- connection = Wifly::Connection.new('localhost', 2000, '2.3.13')
40
+ connection = Wifly::Connection.new('localhost', 2000)
42
41
  connection.socket
43
42
  t.join
44
43
  @server.input.should eq(Wifly::COMMAND_MODE)
@@ -47,9 +46,9 @@ describe Wifly::Connection do
47
46
 
48
47
  it 'should send commands correctly' do
49
48
  t = Thread.new do
50
- @server.receive_command("lites", '123')
49
+ @server.receive_command("lites")
51
50
  end
52
- connection = Wifly::Connection.new('localhost', 2000, '123')
51
+ connection = Wifly::Connection.new('localhost', 2000)
53
52
 
54
53
  result = connection.send_command("lites")
55
54
  t.join
@@ -2,9 +2,9 @@ require 'spec_helper'
2
2
  describe Wifly::Control do
3
3
  it 'should set high and low' do
4
4
  connection = double('connection')
5
- connection.should_receive(:send_command).with("set sys output 0x20 0x20", Wifly::AOK.length)
6
- connection.should_receive(:send_command).with("set sys output 0x00 0x20", Wifly::AOK.length)
7
- control = Wifly::Control.new('localhost', 2000, '1.2', connection)
5
+ connection.should_receive(:send_command).with("set sys output 0x20 0x20")
6
+ connection.should_receive(:send_command).with("set sys output 0x00 0x20")
7
+ control = Wifly::Control.new('localhost', 2000, connection)
8
8
  control.set_high(5)
9
9
  control.set_low(5)
10
10
  end
@@ -12,7 +12,7 @@ describe Wifly::Control do
12
12
  it 'should get high pins' do
13
13
  connection = double('connection')
14
14
  connection.should_receive(:send_command).exactly(2).times.and_return("show io\r\r\n8d08\r\n")
15
- control = Wifly::Control.new('localhost', 2000, '1.2', connection)
15
+ control = Wifly::Control.new('localhost', 2000, connection)
16
16
 
17
17
  control.read_pin(8).should eq(1)
18
18
  control.read_pin(7).should eq(0)
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ #
3
+ # This test excercizes pretty much most of the control methods
4
+ # on an actual WiFly. Supplying the WIFLY_IP as an environment
5
+ # variable activates this test.
6
+ # That means you should have the WiFly connected to a network
7
+ # accessible by the computer running this test.
8
+ #
9
+ if WIFLY_IP
10
+ describe Wifly do
11
+ it 'should really work' do
12
+ 2.times do
13
+ control=Wifly::Control.new(WIFLY_IP, 2000)
14
+ 5.times do |n|
15
+ control.set_high(7)
16
+ control.read_pin(7).should eq(1)
17
+ control.set_low(7)
18
+ control.read_pin(7).should eq(0)
19
+ end
20
+ control.close
21
+ end
22
+ end
23
+ end
24
+ end
@@ -5,6 +5,10 @@
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
7
  require 'wifly'
8
+ require 'pry'
9
+
10
+ # If you set WIFLY_IP, a special "real" test will run against your WiFly.
11
+ WIFLY_IP = ENV['WIFLY_IP'] || nil
8
12
 
9
13
  RSpec.configure do |config|
10
14
  config.treat_symbols_as_metadata_keys_with_true_values = true
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wifly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-25 00:00:00.000000000 Z
12
+ date: 2014-02-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -96,6 +96,7 @@ files:
96
96
  - spec/calculations_spec.rb
97
97
  - spec/connection_spec.rb
98
98
  - spec/control_spec.rb
99
+ - spec/integration_spec.rb
99
100
  - spec/spec_helper.rb
100
101
  - wifly.gemspec
101
102
  homepage: ''
@@ -113,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
114
  version: '0'
114
115
  segments:
115
116
  - 0
116
- hash: 3199720615391178109
117
+ hash: 17962621
117
118
  required_rubygems_version: !ruby/object:Gem::Requirement
118
119
  none: false
119
120
  requirements:
@@ -122,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
123
  version: '0'
123
124
  segments:
124
125
  - 0
125
- hash: 3199720615391178109
126
+ hash: 17962621
126
127
  requirements: []
127
128
  rubyforge_project:
128
129
  rubygems_version: 1.8.25
@@ -133,4 +134,5 @@ test_files:
133
134
  - spec/calculations_spec.rb
134
135
  - spec/connection_spec.rb
135
136
  - spec/control_spec.rb
137
+ - spec/integration_spec.rb
136
138
  - spec/spec_helper.rb