voltronic_power_interface 1.0.0
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 +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: []
|