lxp-packet 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/lxp/packet/read_input1.rb +52 -28
- data/lib/lxp/packet/read_input2.rb +30 -11
- data/lib/lxp/packet/read_input3.rb +16 -4
- data/lib/lxp/utils.rb +3 -5
- data/lib/lxp/version.rb +1 -1
- data/spec/parser_spec.rb +24 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fc4ef9aa204639ee245d39bba281bb3ec005013a7a7d3f16f975d5e31a6eca6
|
4
|
+
data.tar.gz: 9cb28af9771bf98ec9f793bf77785611337529b3514d7b8061d8ee53e234620a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb39026d5d05f414ddfae9d1087646310fb52c1d5a9b59752472eaa69046e793697165b88153e4a03bb9372c562a331e3db4284b312c0164b810b4508d6f052d
|
7
|
+
data.tar.gz: 6bf3e09ff9a48c98c884f6bb45fe776cf15ca80dd6cac1445889c9a9cbb89d3b1631f9df9c18210a1c100af404e0ffbea000af695a767196a6c99ca0824ee1e4
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- support 3 PV strings in `ReadInput1` and `ReadInput2`
|
13
|
+
- support 3 phase power in `ReadInput1`
|
14
|
+
- support `t_bat` key in `ReadInput2#to_h` (suspect this is Lead Acid only as mine is 0)
|
15
|
+
- support `uptime` key in `ReadInput2#to_h`
|
16
|
+
|
17
|
+
### Fixed
|
18
|
+
|
19
|
+
- parsing of temperatures above 255C in ReadInput2
|
20
|
+
|
21
|
+
|
10
22
|
## [0.4.0] - 2020-04-07
|
11
23
|
|
12
24
|
### Added
|
@@ -8,44 +8,68 @@ class LXP
|
|
8
8
|
# Decode the data and return a hash of values in this input packet
|
9
9
|
def to_h
|
10
10
|
{
|
11
|
-
|
11
|
+
# 0 = static 1
|
12
|
+
# 1 = R_INPUT
|
13
|
+
# 2..12 = serial
|
14
|
+
# 13/14 = length
|
12
15
|
|
13
|
-
|
16
|
+
status: Utils.int(@data[15, 2]),
|
17
|
+
|
18
|
+
# 17-22 all observed zeroes (v_pv, v_pv_2, v_pv_3 ?)
|
19
|
+
|
20
|
+
v_bat: Utils.int(@data[23, 2]) / 10.0, # V
|
14
21
|
soc: @data[25], # %
|
22
|
+
# 26 used for anything?
|
23
|
+
|
24
|
+
# observed [0, 47] => 12032 or 0x2F00
|
25
|
+
_unknown_i1_28: @data[28],
|
26
|
+
_unknown_i1_27_28: Utils.int(@data[27, 2]),
|
15
27
|
|
16
|
-
|
28
|
+
p_pv: Utils.int(@data[29, 2]) +
|
29
|
+
Utils.int(@data[31, 2]) +
|
30
|
+
Utils.int(@data[33, 2]), # W
|
31
|
+
p_pv_1: Utils.int(@data[29, 2]), # W
|
32
|
+
p_pv_2: Utils.int(@data[31, 2]), # W
|
33
|
+
p_pv_3: Utils.int(@data[33, 2]), # W
|
34
|
+
p_charge: Utils.int(@data[35, 2]), # W
|
35
|
+
p_discharge: Utils.int(@data[37, 2]), # W
|
36
|
+
v_ac_r: Utils.int(@data[39, 2]) / 10.0, # V
|
37
|
+
v_ac_s: Utils.int(@data[41, 2]) / 10.0, # V
|
38
|
+
v_ac_t: Utils.int(@data[43, 2]) / 10.0, # V
|
39
|
+
f_ac: Utils.int(@data[45, 2]) / 100.0, # Hz
|
17
40
|
|
18
|
-
|
19
|
-
|
20
|
-
p_discharge: Utils.int(@data[37, 2], :lsb), # W
|
21
|
-
v_acr: Utils.int(@data[39, 2], :lsb) / 10.0, # V
|
22
|
-
f_ac: Utils.int(@data[45, 2], :lsb) / 100.0, # Hz
|
41
|
+
p_inv: Utils.int(@data[47, 2]), # W
|
42
|
+
p_rec: Utils.int(@data[49, 2]), # W
|
23
43
|
|
24
|
-
|
25
|
-
p_rec: Utils.int(@data[49, 2], :lsb), # W
|
44
|
+
_unknown_i1_51_52: Utils.int(@data[51, 2]),
|
26
45
|
|
27
|
-
#
|
46
|
+
pf: Utils.int(@data[53, 2]) / 1000.0, # Hz
|
28
47
|
|
29
|
-
|
30
|
-
|
48
|
+
v_eps_r: Utils.int(@data[55, 2]) / 10.0, # V
|
49
|
+
v_eps_s: Utils.int(@data[57, 2]) / 10.0, # V
|
50
|
+
v_eps_t: Utils.int(@data[59, 2]) / 10.0, # V
|
51
|
+
f_eps: Utils.int(@data[61, 2]) / 100.0, # Hz
|
31
52
|
|
32
53
|
# peps and seps in 63..66?
|
33
54
|
|
34
|
-
p_to_grid: Utils.int(@data[67, 2]
|
35
|
-
p_to_user: Utils.int(@data[69, 2]
|
36
|
-
|
37
|
-
e_pv_day: Utils.int(@data[71, 2]
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
p_to_grid: Utils.int(@data[67, 2]), # W
|
56
|
+
p_to_user: Utils.int(@data[69, 2]), # W
|
57
|
+
|
58
|
+
e_pv_day: (Utils.int(@data[71, 2]) +
|
59
|
+
Utils.int(@data[73, 2]) + Utils.int(@data[75, 2])) / 10.0, # kWh
|
60
|
+
e_pv_1_day: Utils.int(@data[71, 2]) / 10.0, # kWh
|
61
|
+
e_pv_2_day: Utils.int(@data[73, 2]) / 10.0, # kWh
|
62
|
+
e_pv_3_day: Utils.int(@data[75, 2]) / 10.0, # kWh
|
63
|
+
e_inv_day: Utils.int(@data[77, 2]) / 10.0, # kWh
|
64
|
+
e_rec_day: Utils.int(@data[79, 2]) / 10.0, # kWh
|
65
|
+
e_chg_day: Utils.int(@data[81, 2]) / 10.0, # kWh
|
66
|
+
e_dischg_day: Utils.int(@data[83, 2]) / 10.0, # kWh
|
67
|
+
e_eps_day: Utils.int(@data[85, 2]) / 10.0, # kWh
|
68
|
+
e_to_grid_day: Utils.int(@data[87, 2]) / 10.0, # kWh
|
69
|
+
e_to_user_day: Utils.int(@data[89, 2]) / 10.0, # kWh
|
70
|
+
|
71
|
+
v_bus_1: Utils.int(@data[91, 2]) / 10.0, # V
|
72
|
+
v_bus_2: Utils.int(@data[93, 2]) / 10.0 # V
|
49
73
|
}
|
50
74
|
end
|
51
75
|
end
|
@@ -8,18 +8,37 @@ class LXP
|
|
8
8
|
# Decode the data and return a hash of values in this input packet
|
9
9
|
def to_h
|
10
10
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
e_dischg_all: Utils.int(@data[39, 4], :lsb) / 10.0, # kWh
|
16
|
-
e_eps_all: Utils.int(@data[43, 4], :lsb) / 10.0, # kWh
|
17
|
-
e_to_grid_all: Utils.int(@data[47, 4], :lsb) / 10.0, # kWh
|
18
|
-
e_to_user_all: Utils.int(@data[51, 4], :lsb) / 10.0, # kWh
|
11
|
+
# 0 = static 1
|
12
|
+
# 1 = R_INPUT
|
13
|
+
# 2..12 = serial
|
14
|
+
# 13/14 = length
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
e_pv_all: (Utils.int(@data[15, 4]) +
|
17
|
+
Utils.int(@data[19, 4]) +
|
18
|
+
Utils.int(@data[23, 4])) / 10.0, # kWh
|
19
|
+
e_pv_1_all: Utils.int(@data[15, 4]) / 10.0, # kWh
|
20
|
+
e_pv_2_all: Utils.int(@data[19, 4]) / 10.0, # kWh
|
21
|
+
e_pv_3_all: Utils.int(@data[23, 4]) / 10.0, # kWh
|
22
|
+
e_inv_all: Utils.int(@data[27, 4]) / 10.0, # kWh
|
23
|
+
e_rec_all: Utils.int(@data[31, 4]) / 10.0, # kWh
|
24
|
+
e_chg_all: Utils.int(@data[35, 4]) / 10.0, # kWh
|
25
|
+
e_dischg_all: Utils.int(@data[39, 4]) / 10.0, # kWh
|
26
|
+
e_eps_all: Utils.int(@data[43, 4]) / 10.0, # kWh
|
27
|
+
e_to_grid_all: Utils.int(@data[47, 4]) / 10.0, # kWh
|
28
|
+
e_to_user_all: Utils.int(@data[51, 4]) / 10.0, # kWh
|
29
|
+
|
30
|
+
# 55 .. 62?
|
31
|
+
# fault code? 4 bytes?
|
32
|
+
# warning code? 4 bytes?
|
33
|
+
|
34
|
+
t_inner: Utils.int(@data[63, 2]),
|
35
|
+
t_rad_1: Utils.int(@data[65, 2]),
|
36
|
+
t_rad_2: Utils.int(@data[67, 2]),
|
37
|
+
t_bat: Utils.int(@data[69, 2]),
|
38
|
+
|
39
|
+
# 71..72 ?
|
40
|
+
|
41
|
+
uptime: Utils.int(@data[73, 4]) # seconds
|
23
42
|
}
|
24
43
|
end
|
25
44
|
end
|
@@ -8,11 +8,20 @@ class LXP
|
|
8
8
|
# Decode the data and return a hash of values in this input packet
|
9
9
|
def to_h
|
10
10
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# 0 = static 1
|
12
|
+
# 1 = R_INPUT
|
13
|
+
# 2..12 = serial
|
14
|
+
# 13/14 = length
|
15
15
|
|
16
|
+
# 15..16? .. (observed: 10)
|
17
|
+
|
18
|
+
max_chg_curr: Utils.int(@data[17, 2]) / 100.0, # A
|
19
|
+
max_dischg_curr: Utils.int(@data[19, 2]) / 100.0, # A
|
20
|
+
charge_volt_ref: Utils.int(@data[21, 2]) / 10.0, # V
|
21
|
+
dischg_cut_volt: Utils.int(@data[23, 2]) / 10.0, # V
|
22
|
+
|
23
|
+
# are these actually 2 bytes as well?
|
24
|
+
# never seen data in them so its hard to tell.
|
16
25
|
bat_status_0: @data[25],
|
17
26
|
bat_status_1: @data[27],
|
18
27
|
bat_status_2: @data[29],
|
@@ -24,6 +33,9 @@ class LXP
|
|
24
33
|
bat_status_8: @data[41],
|
25
34
|
bat_status_9: @data[43],
|
26
35
|
bat_status_inv: @data[45]
|
36
|
+
|
37
|
+
# 47/48 = battery count? seen a 6 but unconfirmed it changes
|
38
|
+
# when battery count does.. TODO..
|
27
39
|
}
|
28
40
|
end
|
29
41
|
end
|
data/lib/lxp/utils.rb
CHANGED
@@ -4,16 +4,14 @@ class LXP
|
|
4
4
|
module Utils
|
5
5
|
module_function
|
6
6
|
|
7
|
-
def int(bytes
|
8
|
-
bytes = bytes.reverse if order == :msb
|
9
|
-
|
7
|
+
def int(bytes)
|
10
8
|
bytes.each_with_index.map do |b, idx|
|
11
9
|
b << (idx * 8)
|
12
10
|
end.inject(:|)
|
13
11
|
end
|
14
12
|
|
15
|
-
def int_complement(bytes
|
16
|
-
r = int(bytes
|
13
|
+
def int_complement(bytes)
|
14
|
+
r = int(bytes)
|
17
15
|
r -= 0x10000 if r & 0x8000 == 0x8000
|
18
16
|
r
|
19
17
|
end
|
data/lib/lxp/version.rb
CHANGED
data/spec/parser_spec.rb
CHANGED
@@ -74,7 +74,30 @@ RSpec.describe LXP::Packet::Parser do
|
|
74
74
|
|
75
75
|
describe '#to_h' do
|
76
76
|
subject { packet.to_h }
|
77
|
-
it { is_expected.to eq status: 16, v_bat: 49.7, soc: 95, p_pv: 0, p_charge: 0, p_discharge: 633,
|
77
|
+
it { is_expected.to eq _unknown_i1_28: 47, _unknown_i1_27_28: 12_032, _unknown_i1_51_52: 227, status: 16, v_bat: 49.7, soc: 95, p_pv: 0, p_pv_1: 0, p_pv_2: 0, p_pv_3: 0, p_charge: 0, p_discharge: 633, v_ac_r: 247.6, v_ac_s: 0.1, v_ac_t: 0.0, f_ac: 50.1, p_inv: 569, p_rec: 0, pf: 1.0, v_eps_r: 247.6, v_eps_s: 281.6, v_eps_t: 2875.2, f_eps: 50.1, p_to_grid: 0, p_to_user: 0, e_pv_day: 22.1, e_pv_1_day: 22.1, e_pv_2_day: 0.0, e_pv_3_day: 0.0, e_inv_day: 4.2, e_rec_day: 7.3, e_chg_day: 8.6, e_dischg_day: 5.2, e_eps_day: 0.0, e_to_grid_day: 3.6, e_to_user_day: 0.2, v_bus_1: 375.6, v_bus_2: 299.7 }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'ReadInput2 data' do
|
82
|
+
let(:input) { [161, 26, 2, 0, 111, 0, 1, 194, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 97, 0, 1, 4, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 40, 0, 80, 83, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 38, 0, 0, 43, 45, 0, 0, 105, 51, 0, 0, 44, 47, 0, 0, 0, 0, 0, 0, 95, 5, 0, 0, 63, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 36, 0, 37, 0, 0, 0, 0, 0, 35, 136, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 199] }
|
83
|
+
|
84
|
+
it { is_expected.to be_a LXP::Packet::ReadInput2 }
|
85
|
+
|
86
|
+
it 'has the correct attributes' do
|
87
|
+
expect(packet).to have_attributes register: 40,
|
88
|
+
values: [83, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 38, 0, 0, 43, 45, 0, 0, 105, 51, 0, 0, 44, 47, 0, 0, 0, 0, 0, 0, 95, 5, 0, 0, 63, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 36, 0, 37, 0, 0, 0, 0, 0, 35, 136, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
89
|
+
protocol: 2,
|
90
|
+
packet_length: 111, data_length: 97,
|
91
|
+
tcp_function: LXP::Packet::TcpFunctions::TRANSLATED_DATA,
|
92
|
+
device_function: LXP::Packet::DeviceFunctions::READ_INPUT,
|
93
|
+
inverter_serial: 'XXXXXXXXXX',
|
94
|
+
datalog_serial: 'cccccccccc',
|
95
|
+
bytes: input
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#to_h' do
|
99
|
+
subject { packet.to_h }
|
100
|
+
it { is_expected.to eq e_chg_all: 1316.1, e_dischg_all: 1207.6, e_eps_all: 0.0, e_inv_all: 990.7, e_pv_all: 1774.7, e_pv_1_all: 1774.7, e_pv_2_all: 0.0, e_pv_3_all: 0.0, e_rec_all: 1156.3, e_to_grid_all: 137.5, e_to_user_all: 1183.9, t_bat: 0, t_inner: 48, t_rad_1: 36, t_rad_2: 37, uptime: 14_714_915 }
|
78
101
|
end
|
79
102
|
end
|
80
103
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lxp-packet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Elsworth
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|