synaccess_connect 0.2.2
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 +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: []
|