voltronic_power_interface 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +71 -0
- data/lib/voltronic/digest.rb +90 -0
- data/lib/voltronic/protocol.rb +95 -0
- data/lib/voltronic/protocols/io.rb +145 -0
- metadata +56 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3a18820cbf50df7c487d5adfc987909141aabdd
|
4
|
+
data.tar.gz: 33b19ddfd04564be7ed903a8171c69aa09427c9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7115f2515c3ff251dfc768da86c70ce24e1699124cbc375dba742e8faddff6893ed0a86f2ec634fe2c0718f511901931e5edd76ce830a745122aaf8281837a2e
|
7
|
+
data.tar.gz: e9f9e3dbb566bdd3abf5a82a97b324b11e412418f9326a812c242fe0aae10651e85be55cbbb9eda4dab82ec12b5f92774dfffaf573e49eb99ac1a2883970c6ed
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Johan van der Vyver
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
## Voltronic Power Interface
|
2
|
+
A library to communicate with [Voltronic Power](http://www.voltronicpower.com/) inverter/PV products.
|
3
|
+
|
4
|
+
**NOTE: The author of this library has no association with the above mentioned company**
|
5
|
+
|
6
|
+
Some [public documentation](https://s3-eu-west-1.amazonaws.com/osor62gd45llv5fcg47yijafsz6dcrjn/Infini_RS232_Protocol.pdf) exists of supported commands and how to interpret the output.
|
7
|
+
|
8
|
+
Not all commands are supported by all devices
|
9
|
+
|
10
|
+
## Use
|
11
|
+
|
12
|
+
### SerialPort
|
13
|
+
|
14
|
+
**NOTE: Only Ruby Gem ```serialport``` is currently supported**
|
15
|
+
|
16
|
+
**Linux/Mac OS X/BSD**
|
17
|
+
|
18
|
+
Serial ports are typically found in /dev/tty*
|
19
|
+
These devices may require root privilege in which case it is recommended to add a udev rule with less restrictive permissions.
|
20
|
+
|
21
|
+
Example udev rule for Prolific devices:
|
22
|
+
|
23
|
+
`echo 'ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE="0666", SYMLINK+="ttyProlific"' > /etc/udev/rules.d/37-prolific.rules`
|
24
|
+
|
25
|
+
require 'voltronic/protocol'
|
26
|
+
# => true
|
27
|
+
|
28
|
+
proto = Voltronic::Protocol.for_serialport('/dev/tty_usbserial')
|
29
|
+
# => Protocol(IO)
|
30
|
+
|
31
|
+
proto.execute 'QPI'
|
32
|
+
#=> '(PI30'
|
33
|
+
|
34
|
+
timeout = 0.5 # 500 milliseconds
|
35
|
+
proto.execute 'QPI', timeout
|
36
|
+
#=> '(PI30'
|
37
|
+
|
38
|
+
**Windows**
|
39
|
+
|
40
|
+
require 'voltronic/protocol'
|
41
|
+
# => true
|
42
|
+
|
43
|
+
proto = Voltronic::Protocol.for_serialport('COM1')
|
44
|
+
# => Protocol(IO)
|
45
|
+
|
46
|
+
proto.execute 'QPI'
|
47
|
+
#=> '(PI30'
|
48
|
+
|
49
|
+
timeout = 0.5 # 500 milliseconds
|
50
|
+
proto.execute 'QPI', timeout
|
51
|
+
#=> '(PI30'
|
52
|
+
|
53
|
+
### USB
|
54
|
+
**The implementation currently only support Linux using HIDRaw**
|
55
|
+
|
56
|
+
To avoid running as root, execute the following to add a symlink with less restricted privileges
|
57
|
+
|
58
|
+
`echo 'ATTRS{idVendor}=="0665", ATTRS{idProduct}=="5161", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="root", SYMLINK+="hidVoltronic"' > /etc/udev/rules.d/35-voltronic.rules`
|
59
|
+
|
60
|
+
require 'voltronic/protocol'
|
61
|
+
# => true
|
62
|
+
|
63
|
+
proto = Voltronic::Protocol.for_usb('/dev/hidVoltronic')
|
64
|
+
# => Protocol(IO)
|
65
|
+
|
66
|
+
proto.execute 'QPI'
|
67
|
+
#=> '(PI30'
|
68
|
+
|
69
|
+
timeout = 0.5 # 500 milliseconds
|
70
|
+
proto.execute 'QPI', timeout
|
71
|
+
#=> '(PI30'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module ::Voltronic
|
2
|
+
module Digest #:nodoc:
|
3
|
+
def self.parse(input) #:nodoc:
|
4
|
+
input = begin
|
5
|
+
input_bytes = input.to_s.bytes
|
6
|
+
end_index = input_bytes.length
|
7
|
+
while(0 != end_index)
|
8
|
+
if (@@termination_character == input_bytes[(end_index -= 1)])
|
9
|
+
break
|
10
|
+
end
|
11
|
+
end
|
12
|
+
input_bytes[0..end_index]
|
13
|
+
end
|
14
|
+
|
15
|
+
if (3 > input.length)
|
16
|
+
raise MalformedInputError.new 'Input must be at at least 3 bytes in size'
|
17
|
+
end
|
18
|
+
|
19
|
+
input_crc = input[-3..-2]
|
20
|
+
input = input[0..-4].map do |byte|
|
21
|
+
if (128 > byte)
|
22
|
+
byte.chr
|
23
|
+
else
|
24
|
+
raise MalformedInputError.new 'Input contained an unparsable character'
|
25
|
+
end
|
26
|
+
end.join
|
27
|
+
|
28
|
+
calculated_crc = encode(input).to_s.bytes[-3..-2]
|
29
|
+
if (calculated_crc == input_crc)
|
30
|
+
return input
|
31
|
+
end
|
32
|
+
|
33
|
+
raise DigestMismatchError.new ['Device digest [',
|
34
|
+
input_crc.first, ',', input_crc.last, '] != [',
|
35
|
+
calculated_crc.first, ',', calculated_crc.last,
|
36
|
+
'] calculated digest'].join
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.encode(input) #:nodoc:
|
40
|
+
input = input.to_s
|
41
|
+
crc = 0
|
42
|
+
input.bytes.each do |byte|
|
43
|
+
crc = ((@@crc_table[(((crc >> 8) ^ byte) & 0xff)] ^ (crc << 8)) & 0xffff)
|
44
|
+
end
|
45
|
+
|
46
|
+
first_byte = ((crc & 0xff00) >> 8)
|
47
|
+
second_byte = (crc & 0xff)
|
48
|
+
|
49
|
+
if ((0x28 == first_byte) || (0x0d == first_byte) || (0x0a == first_byte))
|
50
|
+
first_byte += 1
|
51
|
+
end
|
52
|
+
if ((0x28 == second_byte) || (0x0d == second_byte) || (0x0a == second_byte))
|
53
|
+
second_byte += 1
|
54
|
+
end
|
55
|
+
|
56
|
+
[input, first_byte.chr, second_byte.chr, @@termination_character.chr].join
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.eos?(input) #:nodoc:
|
60
|
+
input.to_s.bytes.any? { |byte| (@@termination_character == byte) }
|
61
|
+
end
|
62
|
+
|
63
|
+
@@termination_character ||= "\r".bytes.first #:nodoc:
|
64
|
+
|
65
|
+
@@crc_table ||= [ #:nodoc:
|
66
|
+
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c,
|
67
|
+
0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318,
|
68
|
+
0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4,
|
69
|
+
0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630,
|
70
|
+
0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4,
|
71
|
+
0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
|
72
|
+
0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf,
|
73
|
+
0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
74
|
+
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13,
|
75
|
+
0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9,
|
76
|
+
0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046,
|
77
|
+
0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
|
78
|
+
0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2,
|
79
|
+
0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e,
|
80
|
+
0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e,
|
81
|
+
0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
82
|
+
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1,
|
83
|
+
0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
|
84
|
+
0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9,
|
85
|
+
0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 ].freeze
|
86
|
+
|
87
|
+
class DigestMismatchError < RuntimeError; end
|
88
|
+
class MalformedInputError < RuntimeError; end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module ::Voltronic
|
2
|
+
module Protocol
|
3
|
+
require 'voltronic/digest'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Open a connection to the device
|
7
|
+
# - Closes an existing connection, if one exists
|
8
|
+
|
9
|
+
##
|
10
|
+
# Create a new Protocol object using an input IO object
|
11
|
+
def self.for_io(io)
|
12
|
+
require 'voltronic/protocols/io'
|
13
|
+
|
14
|
+
def self.for_io(io)
|
15
|
+
::Voltronic::Protocol::IO.method(:new).call(io)
|
16
|
+
end
|
17
|
+
|
18
|
+
for_io(io)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Create a new Protocol object for Axpert device connected to a serial port
|
23
|
+
def self.for_serialport(port_or_dev, baud = 2400, data_bits = 8, stop_bits = 1, parity = :none)
|
24
|
+
begin
|
25
|
+
require 'serialport'
|
26
|
+
rescue Exception
|
27
|
+
raise LoadError.new 'RubyGem "serialport" required to make use of Axpert on a Serial Port'
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.for_serialport(port_or_dev, baud = 2400, data_bits = 8, stop_bits = 1, parity = :none)
|
31
|
+
port_or_dev = port_or_dev.to_s.strip
|
32
|
+
|
33
|
+
baud = begin
|
34
|
+
parse = Integer(baud)
|
35
|
+
raise if (1 > parse)
|
36
|
+
parse
|
37
|
+
rescue
|
38
|
+
raise ArgumentError.new "Invalid baud #{baud}"
|
39
|
+
end
|
40
|
+
|
41
|
+
data_bits = begin
|
42
|
+
parse = Integer(data_bits)
|
43
|
+
raise unless ((5 == parse) || (6 == parse) || (7 == parse) || (8 == parse))
|
44
|
+
parse
|
45
|
+
rescue
|
46
|
+
raise ArgumentError.new "Invalid data bits #{data_bits}"
|
47
|
+
end
|
48
|
+
|
49
|
+
stop_bits = begin
|
50
|
+
parse = Integer(stop_bits)
|
51
|
+
raise unless ((1 == parse) || (2 == parse))
|
52
|
+
parse
|
53
|
+
rescue
|
54
|
+
raise ArgumentError.new "Invalid stop bits #{stop_bits}"
|
55
|
+
end
|
56
|
+
|
57
|
+
parity = begin
|
58
|
+
parse = parity.to_s.strip.upcase
|
59
|
+
raise unless (('NONE' == parse) || ('EVEN' == parse) || ('ODD' == parse) || ('MARK' == parse) || ('SPACE' == parse))
|
60
|
+
::SerialPort.const_get(parse.to_sym)
|
61
|
+
rescue
|
62
|
+
raise ArgumentError.new "Invalid Parity #{parity}"
|
63
|
+
end
|
64
|
+
|
65
|
+
serial_io = ::SerialPort.new(port_or_dev, baud, data_bits, stop_bits, parity)
|
66
|
+
serial_io.read_timeout = -1
|
67
|
+
for_io(serial_io)
|
68
|
+
end
|
69
|
+
|
70
|
+
for_serialport(port_or_dev, baud, data_bits, stop_bits, parity)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Create a new Protocol object for Axpert device connected to USB
|
75
|
+
#
|
76
|
+
# Currently only supported on Linux kernels that include HIDRaw
|
77
|
+
def self.for_usb(dev)
|
78
|
+
if !(RUBY_PLATFORM =~ /linux/)
|
79
|
+
raise NotImplementedError.new 'USB is currently only supported in Linux'
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.for_usb(dev)
|
83
|
+
for_io(::File.open(dev.to_s, (::IO::RDWR | ::IO::NONBLOCK)))
|
84
|
+
end
|
85
|
+
|
86
|
+
for_usb(dev)
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Execute a query on the device
|
91
|
+
def execute(input, timeout = 1.0)
|
92
|
+
raise NotImplementedError.new 'Protocol implementation does not implement execute(..)'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'voltronic/protocol'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Implementation of the Protocol for IO objects
|
5
|
+
class ::Voltronic::Protocol::IO #:nodoc:
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
MAXIMUM_RESPONSE_SIZE = 1024
|
9
|
+
READ_BLOCK_SIZE = 8 # USB device report size
|
10
|
+
READ_OPTIONS = { exception: false }.freeze
|
11
|
+
CLEAR_TIMEOUT = 2.0
|
12
|
+
|
13
|
+
def initialize(io) #:nodoc:
|
14
|
+
@io_array = [io].freeze
|
15
|
+
|
16
|
+
@io = begin # Verify input io quacks like an IO
|
17
|
+
begin
|
18
|
+
io.sync = true
|
19
|
+
rescue Exception
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
io.binmode = true
|
24
|
+
rescue Exception
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
io.read_nonblock(READ_BLOCK_SIZE, nil, READ_OPTIONS)
|
29
|
+
rescue Exception
|
30
|
+
raise ArgumentError.new [io.to_s, ' does not support read_nonblock'].join
|
31
|
+
end
|
32
|
+
|
33
|
+
if !io.respond_to?(:write)
|
34
|
+
raise ArgumentError.new [io.to_s, ' does not support write'].join
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
::IO.select(@io_array, nil, nil, 0)
|
39
|
+
rescue Exception
|
40
|
+
raise ArgumentError.new [io.to_s, ' does not support IO.select(', io.to_s, ')'].join
|
41
|
+
end
|
42
|
+
|
43
|
+
io
|
44
|
+
end
|
45
|
+
|
46
|
+
@support_flush ||= begin # Assert if flush works
|
47
|
+
@io.flush
|
48
|
+
true
|
49
|
+
rescue Exception
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
clear_read_buffer
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute(input, timeout = 1.0) #:nodoc:
|
57
|
+
timeout = begin
|
58
|
+
parse = Float(timeout)
|
59
|
+
raise if (0 >= parse)
|
60
|
+
parse
|
61
|
+
rescue
|
62
|
+
raise ArgumentError.new ['Invalid timeout ', timeout.to_s].join
|
63
|
+
end
|
64
|
+
|
65
|
+
clear_read_buffer
|
66
|
+
write(input)
|
67
|
+
read(timeout)
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s #:nodoc:
|
71
|
+
'Protocol(IO)'
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect #:nodoc:
|
75
|
+
self.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def close
|
79
|
+
@io.close
|
80
|
+
true
|
81
|
+
rescue Exception
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def closed?
|
86
|
+
@io.closed?
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def clear_read_buffer #:nodoc:
|
92
|
+
loop_timeout = Time.now.to_f + CLEAR_TIMEOUT
|
93
|
+
|
94
|
+
while(true)
|
95
|
+
if ::IO.select(@io_array, nil, nil, 0).nil?
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
|
99
|
+
@io.read_nonblock(READ_BLOCK_SIZE, nil, READ_OPTIONS)
|
100
|
+
|
101
|
+
if (Time.now.to_f > loop_timeout)
|
102
|
+
raise DeviceError.new 'Device is responding with binary data before a write has been issued'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def read(timeout) #:nodoc:
|
108
|
+
blocks = ''
|
109
|
+
loop_timeout = Time.now.to_f + timeout
|
110
|
+
while(true)
|
111
|
+
iteration_timeout = (loop_timeout - Time.now.to_f)
|
112
|
+
if (0 >= iteration_timeout)
|
113
|
+
raise TimeoutError.new ['Timeout of ', timeout.to_s, ' seconds reached'].join
|
114
|
+
end
|
115
|
+
|
116
|
+
next if ::IO.select(@io_array, nil, nil, iteration_timeout).nil?
|
117
|
+
|
118
|
+
block = @io.read_nonblock(READ_BLOCK_SIZE, nil, READ_OPTIONS)
|
119
|
+
next if block.nil?
|
120
|
+
blocks << block
|
121
|
+
|
122
|
+
if ::Voltronic::Digest.eos?(block)
|
123
|
+
return ::Voltronic::Digest.parse(blocks)
|
124
|
+
end
|
125
|
+
|
126
|
+
if (blocks.length > MAXIMUM_RESPONSE_SIZE)
|
127
|
+
raise BufferOverflowError.new ['Device response exceeds the maximum of ', MAXIMUM_RESPONSE_SIZE.to_s, ' bytes'].join
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def write(input) #:nodoc:
|
133
|
+
number_of_bytes = @io.write(::Voltronic::Digest.encode(input))
|
134
|
+
if @support_flush
|
135
|
+
@io.flush
|
136
|
+
end
|
137
|
+
number_of_bytes
|
138
|
+
end
|
139
|
+
|
140
|
+
class TimeoutError < RuntimeError; end #:nodoc:
|
141
|
+
class BufferOverflowError < RuntimeError; end #:nodoc:
|
142
|
+
class DeviceError < RuntimeError; end #:nodoc:
|
143
|
+
|
144
|
+
private_class_method :new
|
145
|
+
end
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: voltronic_power_interface
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Johan van der Vyver
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Ruby library to communicate with Voltronic Power devices
|
14
|
+
email: code@johan.vdvyver.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files:
|
18
|
+
- LICENSE
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- lib/voltronic/digest.rb
|
23
|
+
- lib/voltronic/protocol.rb
|
24
|
+
- lib/voltronic/protocols/io.rb
|
25
|
+
homepage: https://github.com/jovandervyver/voltronic_power_interface
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options:
|
31
|
+
- "--quiet"
|
32
|
+
- "--line-numbers"
|
33
|
+
- "--inline-source"
|
34
|
+
- "--title"
|
35
|
+
- Voltronic Power Interface
|
36
|
+
- "--main"
|
37
|
+
- README.rdoc
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.9.3
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 2.6.8
|
53
|
+
signing_key:
|
54
|
+
specification_version: 4
|
55
|
+
summary: Voltronic Power Interface
|
56
|
+
test_files: []
|