synaccess_connect 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +39 -0
- data/lib/synaccess_connect/net_booter/error.rb +4 -0
- data/lib/synaccess_connect/net_booter/http/http_connection.rb +128 -0
- data/lib/synaccess_connect/net_booter/http/rev_a.rb +24 -0
- data/lib/synaccess_connect/net_booter/http/rev_b.rb +20 -0
- data/lib/synaccess_connect/net_booter/telnet/rev_a.rb +23 -0
- data/lib/synaccess_connect/net_booter/telnet/rev_b.rb +17 -0
- data/lib/synaccess_connect/net_booter/telnet/telnet_connection.rb +119 -0
- data/lib/synaccess_connect.rb +34 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZmRjYmQ0NmU2ZTE2Y2IwZTZiOGNjZGUwYTgyM2M5ZTk2MTIxNGI1OQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YTkyZGExYmZiMjA3ZDcxMTRlZjk4NjA0YzRlMTU5NThlYTU2YjUwNg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MmU2NWFhMGVjMDg2MTdkMWZiMTA0MThlZmY2MTU4OGE1OGMzNGY0Y2ZmZjQ0
|
10
|
+
NzRhMGMzZTExNGRjYjZmMjBhZDdiNGZkNDA0MDEyZWU5OWRjYzI0NGVhNDQ0
|
11
|
+
MjM1YWJlNWYwODQxZTUyZDVjNTNiZWM4ZjZkNjMzMjBhZDI2ZGE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ODg5NGEwNjdjYTIyMzU2YzgwNjQwNmY1NTVmZDdlOGMyZDk4MTAyODcwOTE2
|
14
|
+
ZDcyOTQzZjE0MWIxY2YxN2VjMzgzYTgzYzJhYmZmNzhmY2E0YWMwNzIxOGU5
|
15
|
+
MjJkNDE4OGM2OWZmMzJiNDQ0YWNkMzNiN2VjNGU5MGRkYWNjNjg=
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Synaccess Connect
|
2
|
+
|
3
|
+
Communication wrappers for Synaccess netBooter power relays.
|
4
|
+
|
5
|
+
## Supported Firmware:
|
6
|
+
* NP-02
|
7
|
+
* NP-02B
|
8
|
+
|
9
|
+
## Available interfaces
|
10
|
+
```
|
11
|
+
NetBooter
|
12
|
+
Http
|
13
|
+
RevA
|
14
|
+
RevB
|
15
|
+
Telnet
|
16
|
+
RevA
|
17
|
+
RevB
|
18
|
+
```
|
19
|
+
|
20
|
+
## New connections
|
21
|
+
|
22
|
+
`connection = NetBooter::PROTOCOL::RevX.new('XXX.XXX.XXX.XXX', options)`
|
23
|
+
|
24
|
+
Options:
|
25
|
+
|
26
|
+
* username
|
27
|
+
* password
|
28
|
+
|
29
|
+
## Interface
|
30
|
+
|
31
|
+
All classes should implement the following methods:
|
32
|
+
|
33
|
+
* `initialize(host, options)`
|
34
|
+
* `status(outlet) => boolean`
|
35
|
+
* `statuses => Hash of { outlet => boolean }`
|
36
|
+
* `toggle(new_status, outlet) => new status boolean`
|
37
|
+
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
class NetBooter::HttpConnection
|
5
|
+
def initialize(host, options = {})
|
6
|
+
@host = host
|
7
|
+
@options = {
|
8
|
+
:port => 80
|
9
|
+
}.merge(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get the status of an outlet
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
# >> netbooter.status(2)
|
16
|
+
# => false
|
17
|
+
#
|
18
|
+
# Arguments:
|
19
|
+
# +outlet+ Outlet number you want to check (1-based)
|
20
|
+
#
|
21
|
+
# Returns:
|
22
|
+
# boolean - on/off status of the outlet
|
23
|
+
def status(outlet = 1)
|
24
|
+
statuses.fetch outlet do
|
25
|
+
raise NetBooter::Error.new('Error communicating with relay')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Turn on the specified outlet
|
30
|
+
# Example:
|
31
|
+
# >> netbooter.toggle_on(2)
|
32
|
+
# => true
|
33
|
+
#
|
34
|
+
# Arguments:
|
35
|
+
# +outlet+ Outlet you want to turn on (1-based)
|
36
|
+
#
|
37
|
+
# Returns:
|
38
|
+
# boolean - The new status of the outlet (should be true)
|
39
|
+
def toggle_on(outlet = 1)
|
40
|
+
toggle(outlet, true)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Turn off the specified outlet
|
44
|
+
# Example:
|
45
|
+
# >> netbooter.toggle_off(2)
|
46
|
+
# => true
|
47
|
+
#
|
48
|
+
# Arguments:
|
49
|
+
# +outlet+ Outlet you want to turn off (1-based)
|
50
|
+
#
|
51
|
+
# Returns:
|
52
|
+
# boolean - The new status of the outlet (should be false)
|
53
|
+
def toggle_off(outlet = 1)
|
54
|
+
toggle(outlet, false)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Toggle the status of an outlet
|
58
|
+
#
|
59
|
+
# Example:
|
60
|
+
# >> netbooter.toggle(1, false)
|
61
|
+
# => false
|
62
|
+
#
|
63
|
+
# Arguments:
|
64
|
+
# +outlet+ The outlet you want to toggle
|
65
|
+
# +status+ Boolean. true to turn on, false to turn off
|
66
|
+
#
|
67
|
+
# Returns:
|
68
|
+
# boolean - The new status of the outlet
|
69
|
+
def toggle(outlet, status)
|
70
|
+
current_status = status(outlet)
|
71
|
+
toggle_relay(outlet) if current_status != status
|
72
|
+
status
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get the status of all outlets on the device. Needs to be defined
|
76
|
+
# in a subclass.
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
# >> netbooter.statuses
|
80
|
+
# => { 1 => true, 2 => false }
|
81
|
+
#
|
82
|
+
# Arguments: none
|
83
|
+
#
|
84
|
+
# Returns:
|
85
|
+
# A hash containing the outlet numbers and their respective status
|
86
|
+
def statuses
|
87
|
+
raise NotImplementedError.new
|
88
|
+
end
|
89
|
+
|
90
|
+
# The method that contains the network command the relay. Needs to be defined
|
91
|
+
# in a subclass.
|
92
|
+
def toggle_relay(outlet)
|
93
|
+
raise NotImplementedError.new('Must implement toggle_relay in subclass')
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Make an http request and return the result.
|
99
|
+
def get_request(path)
|
100
|
+
resp = nil
|
101
|
+
begin
|
102
|
+
Timeout::timeout(5) do
|
103
|
+
resp = do_http_request(path)
|
104
|
+
end
|
105
|
+
rescue => e
|
106
|
+
raise NetBooter::Error.new("Error connecting to relay: #{e.message}")
|
107
|
+
end
|
108
|
+
resp
|
109
|
+
end
|
110
|
+
|
111
|
+
def do_http_request(path)
|
112
|
+
resp = nil
|
113
|
+
Net::HTTP.start(@host) do |http|
|
114
|
+
req = Net::HTTP::Get.new(path)
|
115
|
+
|
116
|
+
req.basic_auth @options[:username], @options[:password] if @options[:username] && @options[:password]
|
117
|
+
|
118
|
+
resp = http.request(req)
|
119
|
+
|
120
|
+
# Error checking. Allow 200s and 302s
|
121
|
+
unless ['200', '302'].include? resp.code
|
122
|
+
raise NetBooter::Error.new "Relay responded with #{resp.code}. Perhaps you have the wrong relay type specified."
|
123
|
+
end
|
124
|
+
end
|
125
|
+
resp
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class NetBooter::Http::RevA < NetBooter::HttpConnection
|
2
|
+
# So here's the deal... When you hit the switch page (pwrSw1.cgi) nothing happens.
|
3
|
+
# It's only when hitting the status page AFTER visiting a swith page that
|
4
|
+
# the relay is toggled.
|
5
|
+
def toggle_relay(outlet)
|
6
|
+
get_request("/pwrSw#{outlet}.cgi")
|
7
|
+
get_request("/synOpStatus.shtml")
|
8
|
+
end
|
9
|
+
|
10
|
+
def statuses
|
11
|
+
resp = get_request('/synOpStatus.shtml')
|
12
|
+
|
13
|
+
doc = Nokogiri::HTML(resp.body)
|
14
|
+
nodes = doc.xpath('//img')
|
15
|
+
status = {}
|
16
|
+
|
17
|
+
nodes.each do |node|
|
18
|
+
if node.values[0].match(/^led(off|on).gif$/)
|
19
|
+
status[node.parent.parent.xpath('th').first.content.to_i] = $1 == 'on' ? true : false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
status
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class NetBooter::Http::RevB < NetBooter::HttpConnection
|
2
|
+
def toggle_relay(outlet)
|
3
|
+
get_request("/cmd.cgi?rly=#{outlet - 1}")
|
4
|
+
end
|
5
|
+
|
6
|
+
def statuses
|
7
|
+
resp = get_request('/status.xml')
|
8
|
+
|
9
|
+
doc = Nokogiri::XML(resp.body)
|
10
|
+
nodes = doc.xpath('/response/*')
|
11
|
+
|
12
|
+
status = {}
|
13
|
+
nodes.each do |node|
|
14
|
+
if node.name.match(/^rly(\d+)$/)
|
15
|
+
status[$1.to_i + 1] = node.content == '1' ? true : false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
status
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class NetBooter::Telnet::RevA < NetBooter::TelnetConnection
|
2
|
+
|
3
|
+
def override_options
|
4
|
+
{
|
5
|
+
:prompt => /^\r>$/n,
|
6
|
+
:status_string => 'On'
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def local_statuses
|
11
|
+
parse_status @connection.cmd("pshow")
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def authenticate
|
16
|
+
sleep 0.1
|
17
|
+
@connection.puts(@options[:username])
|
18
|
+
sleep 0.1
|
19
|
+
@connection.puts(@options[:password])
|
20
|
+
@connection.cmd('')
|
21
|
+
sleep 0.1
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class NetBooter::Telnet::RevB < NetBooter::TelnetConnection
|
2
|
+
def local_statuses
|
3
|
+
@connection.cmd("")
|
4
|
+
sleep 0.25
|
5
|
+
parse_status @connection.cmd("pshow")
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def authenticate
|
11
|
+
@connection.cmd('login')
|
12
|
+
sleep 0.25
|
13
|
+
@connection.puts(@options[:username])
|
14
|
+
sleep 0.25
|
15
|
+
@connection.puts(@options[:password])
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'net/telnet'
|
2
|
+
|
3
|
+
class NetBooter::TelnetConnection
|
4
|
+
def initialize(host, options = {})
|
5
|
+
@host = host
|
6
|
+
@options = { :port => 23,
|
7
|
+
:binmode => false,
|
8
|
+
:prompt => /^[>]/n,
|
9
|
+
:timeout => 1,
|
10
|
+
:status_string => 'ON'
|
11
|
+
}.merge(override_options).merge(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def override_options
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def status(outlet = 1)
|
19
|
+
statuses.fetch outlet do
|
20
|
+
raise NetBooter::Error.new('Error communicating with relay')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def statuses
|
25
|
+
with_connection :default => {} do
|
26
|
+
local_statuses
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def toggle_on(outlet = 1)
|
31
|
+
toggle(true, outlet)
|
32
|
+
end
|
33
|
+
|
34
|
+
def toggle_off(outlet = 1)
|
35
|
+
toggle(false, outlet)
|
36
|
+
end
|
37
|
+
|
38
|
+
def toggle(outlet, status)
|
39
|
+
status_string = status ? '1' : '0'
|
40
|
+
with_connection do
|
41
|
+
@connection.cmd("pset #{outlet} #{status_string}")
|
42
|
+
end
|
43
|
+
status
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def authenticate
|
49
|
+
raise NotImplementedException.new('Must implement in subclass')
|
50
|
+
end
|
51
|
+
|
52
|
+
# Parse the text that we get back from pshow
|
53
|
+
# returns hash of { channel => boolean_status }
|
54
|
+
def parse_status(response)
|
55
|
+
statuses = {}
|
56
|
+
return statuses unless response
|
57
|
+
|
58
|
+
response.split("\n").each do |line|
|
59
|
+
parts = line.split('|').map &:strip
|
60
|
+
next unless parts[0] =~ /^\d+/
|
61
|
+
statuses[parts[0].to_i] = parts[2] == @options[:status_string]
|
62
|
+
end
|
63
|
+
statuses
|
64
|
+
end
|
65
|
+
|
66
|
+
def connect
|
67
|
+
retry_block 5 do
|
68
|
+
@connection = Net::Telnet::new(
|
69
|
+
'Host' => @host,
|
70
|
+
'Port' => @options[:port],
|
71
|
+
'Binmode' => @options[:binmode],
|
72
|
+
'Prompt' => @options[:prompt],
|
73
|
+
'Timeout' => @options[:timeout]
|
74
|
+
)
|
75
|
+
authenticate if @options[:username] && @options[:password]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def disconnect
|
80
|
+
begin
|
81
|
+
@connection.close if @connection
|
82
|
+
rescue
|
83
|
+
# do nothing
|
84
|
+
end
|
85
|
+
# needs a little bit of delay after disconnecting
|
86
|
+
# if so the next connection doesn't get refused while
|
87
|
+
# this one is closing. Number arrived at by experimentation
|
88
|
+
sleep 0.02
|
89
|
+
end
|
90
|
+
|
91
|
+
def with_connection options = {}
|
92
|
+
begin
|
93
|
+
connect
|
94
|
+
raise NetBooter::Error.new('Unable to connect') if @connection.nil?
|
95
|
+
output = yield
|
96
|
+
rescue => e
|
97
|
+
puts "ERROR! #{e.message}"
|
98
|
+
# puts e.backtrace.join("\n")
|
99
|
+
output = options[:default] if options[:default]
|
100
|
+
ensure
|
101
|
+
disconnect
|
102
|
+
end
|
103
|
+
|
104
|
+
output
|
105
|
+
end
|
106
|
+
|
107
|
+
def retry_block max_attempts, options = {}
|
108
|
+
max_attempts.times do |i|
|
109
|
+
begin
|
110
|
+
yield
|
111
|
+
break # if we made it through the yield without an error we can break
|
112
|
+
rescue => e
|
113
|
+
puts e.message
|
114
|
+
raise e if i + 1 >= max_attempts
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module NetBooter
|
2
|
+
module Telnet
|
3
|
+
end
|
4
|
+
module Http
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'synaccess_connect/net_booter/error'
|
9
|
+
require 'synaccess_connect/net_booter/telnet/telnet_connection'
|
10
|
+
require 'synaccess_connect/net_booter/telnet/rev_a'
|
11
|
+
require 'synaccess_connect/net_booter/telnet/rev_b'
|
12
|
+
|
13
|
+
require 'synaccess_connect/net_booter/http/http_connection'
|
14
|
+
require 'synaccess_connect/net_booter/http/rev_a'
|
15
|
+
require 'synaccess_connect/net_booter/http/rev_b'
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# if defined?(Rails)
|
20
|
+
# module SynaccessConnect
|
21
|
+
# class Railtie < Rails::Railtie
|
22
|
+
# # initializer "carrierwave.setup_paths" do
|
23
|
+
# # CarrierWave.root = Rails.root.join(Rails.public_path).to_s
|
24
|
+
# # CarrierWave.base_path = ENV['RAILS_RELATIVE_URL_ROOT']
|
25
|
+
# # end
|
26
|
+
|
27
|
+
# # initializer "carrierwave.active_record" do
|
28
|
+
# # ActiveSupport.on_load :active_record do
|
29
|
+
# # require 'carrierwave/orm/activerecord'
|
30
|
+
# # end
|
31
|
+
# # end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: synaccess_connect
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jason Hanggi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: vcr
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - <
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.16'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - <
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.16'
|
69
|
+
description: Ruby interface to connecting to Synaccess netBooter power relays
|
70
|
+
email:
|
71
|
+
- jason@tablexi.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- README.md
|
77
|
+
- lib/synaccess_connect.rb
|
78
|
+
- lib/synaccess_connect/net_booter/error.rb
|
79
|
+
- lib/synaccess_connect/net_booter/http/http_connection.rb
|
80
|
+
- lib/synaccess_connect/net_booter/http/rev_a.rb
|
81
|
+
- lib/synaccess_connect/net_booter/http/rev_b.rb
|
82
|
+
- lib/synaccess_connect/net_booter/telnet/rev_a.rb
|
83
|
+
- lib/synaccess_connect/net_booter/telnet/rev_b.rb
|
84
|
+
- lib/synaccess_connect/net_booter/telnet/telnet_connection.rb
|
85
|
+
homepage: https://github.com/tablexi/synaccess
|
86
|
+
licenses: []
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.2.2
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: ''
|
108
|
+
test_files: []
|