waterfurnace_aurora 0.3.13 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/aurora_fetch +4 -22
- data/exe/aurora_mock +16 -16
- data/exe/aurora_monitor +25 -18
- data/exe/aurora_mqtt_bridge +107 -39
- data/exe/web_aid_tool +10 -53
- data/lib/aurora/abc_client.rb +108 -27
- data/lib/aurora/blower.rb +99 -0
- data/lib/aurora/component.rb +17 -0
- data/lib/aurora/mock_abc.rb +49 -0
- data/lib/aurora/pump.rb +60 -0
- data/lib/aurora/registers.rb +170 -64
- data/lib/aurora/thermostat.rb +3 -5
- data/lib/aurora/version.rb +1 -1
- data/lib/aurora.rb +1 -2
- metadata +6 -2
data/lib/aurora/abc_client.rb
CHANGED
@@ -1,39 +1,88 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "yaml"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
require "aurora/blower"
|
7
|
+
require "aurora/iz2_zone"
|
8
|
+
require "aurora/pump"
|
9
|
+
require "aurora/thermostat"
|
10
|
+
|
3
11
|
module Aurora
|
4
12
|
class ABCClient
|
13
|
+
class << self
|
14
|
+
def open_modbus_slave(uri)
|
15
|
+
uri = URI.parse(uri)
|
16
|
+
|
17
|
+
io = case uri.scheme
|
18
|
+
when "tcp"
|
19
|
+
require "socket"
|
20
|
+
TCPSocket.new(uri.host, uri.port)
|
21
|
+
when "telnet", "rfc2217"
|
22
|
+
require "net/telnet/rfc2217"
|
23
|
+
Net::Telnet::RFC2217.new(uri.host,
|
24
|
+
port: uri.port || 23,
|
25
|
+
baud: 19_200,
|
26
|
+
parity: :even)
|
27
|
+
|
28
|
+
else
|
29
|
+
return Aurora::MockABC.new(YAML.load_file(uri.path)) if File.file?(uri.path)
|
30
|
+
|
31
|
+
require "ccutrer-serialport"
|
32
|
+
CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)
|
33
|
+
end
|
34
|
+
|
35
|
+
client = ::ModBus::RTUClient.new(io)
|
36
|
+
client.with_slave(1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
5
40
|
attr_reader :modbus_slave,
|
6
41
|
:serial_number,
|
7
42
|
:zones,
|
43
|
+
:blower,
|
44
|
+
:pump,
|
8
45
|
:faults,
|
9
46
|
:current_mode,
|
10
|
-
:
|
47
|
+
:dhw_enabled,
|
48
|
+
:dhw_setpoint,
|
11
49
|
:entering_air_temperature,
|
12
50
|
:relative_humidity,
|
13
51
|
:leaving_air_temperature,
|
14
52
|
:leaving_water_temperature,
|
15
53
|
:entering_water_temperature,
|
16
54
|
:dhw_water_temperature,
|
17
|
-
:waterflow,
|
18
55
|
:compressor_speed,
|
19
56
|
:outdoor_temperature,
|
20
57
|
:fp1,
|
21
58
|
:fp2,
|
22
|
-
:blower_only_ecm_speed,
|
23
|
-
:aux_heat_ecm_speed,
|
24
59
|
:compressor_watts,
|
25
|
-
:blower_watts,
|
26
60
|
:aux_heat_watts,
|
27
|
-
:loop_pump_watts,
|
28
61
|
:total_watts
|
29
62
|
|
30
|
-
def initialize(
|
31
|
-
@modbus_slave =
|
63
|
+
def initialize(uri)
|
64
|
+
@modbus_slave = self.class.open_modbus_slave(uri)
|
32
65
|
@modbus_slave.read_retry_timeout = 15
|
33
66
|
@modbus_slave.read_retries = 2
|
34
|
-
|
35
|
-
registers =
|
36
|
-
@
|
67
|
+
raw_registers = @modbus_slave.holding_registers[88..91, 105...110, 404, 412..413, 1114]
|
68
|
+
registers = Aurora.transform_registers(raw_registers.dup)
|
69
|
+
@program = registers[88]
|
70
|
+
@serial_number = registers[105]
|
71
|
+
@dhw_water_temperature = registers[1114]
|
72
|
+
@energy_monitor = raw_registers[412]
|
73
|
+
|
74
|
+
@blower = case raw_registers[404]
|
75
|
+
when 1, 2 then Blower::ECM.new(self, registers[404])
|
76
|
+
when 3 then Blower::FiveSpeed.new(self, registers[404])
|
77
|
+
else; Blower::PSC.new(self, registers[404])
|
78
|
+
end
|
79
|
+
@pump = if (3..5).include?(raw_registers[413])
|
80
|
+
Pump::VSPump.new(self,
|
81
|
+
registers[413])
|
82
|
+
else
|
83
|
+
Pump::GenericPump.new(self,
|
84
|
+
registers[413])
|
85
|
+
end
|
37
86
|
|
38
87
|
@zones = if iz2?
|
39
88
|
iz2_zone_count = @modbus_slave.holding_registers[483]
|
@@ -79,8 +128,13 @@ module Aurora
|
|
79
128
|
end
|
80
129
|
|
81
130
|
def refresh
|
82
|
-
registers_to_read = [6, 19..20, 25, 30,
|
83
|
-
|
131
|
+
registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114, 1147..1153, 1165,
|
132
|
+
31_003]
|
133
|
+
registers_to_read << (400..401) if dhw?
|
134
|
+
registers_to_read.concat(blower.registers_to_read)
|
135
|
+
registers_to_read.concat(pump.registers_to_read)
|
136
|
+
registers_to_read.concat([362, 3001]) if vs_drive?
|
137
|
+
|
84
138
|
if zones.first.is_a?(IZ2Zone)
|
85
139
|
zones.each_with_index do |_z, i|
|
86
140
|
base1 = 21_203 + i * 9
|
@@ -100,15 +154,25 @@ module Aurora
|
|
100
154
|
registers = @modbus_slave.holding_registers[*registers_to_read]
|
101
155
|
Aurora.transform_registers(registers)
|
102
156
|
|
103
|
-
|
157
|
+
outputs = registers[30]
|
158
|
+
|
159
|
+
@dhw_enabled = registers[400]
|
160
|
+
@dhw_setpoint = registers[401]
|
104
161
|
@entering_air_temperature = registers[740]
|
105
162
|
@relative_humidity = registers[741]
|
106
163
|
@leaving_air_temperature = registers[900]
|
107
164
|
@leaving_water_temperature = registers[1110]
|
108
165
|
@entering_water_temperature = registers[1111]
|
109
166
|
@dhw_water_temperature = registers[1114]
|
110
|
-
@
|
111
|
-
|
167
|
+
@compressor_speed = if vs_drive?
|
168
|
+
registers[3001]
|
169
|
+
elsif outputs.include?(:cc2)
|
170
|
+
2
|
171
|
+
elsif outputs.include?(:cc)
|
172
|
+
1
|
173
|
+
else
|
174
|
+
0
|
175
|
+
end
|
112
176
|
@outdoor_temperature = registers[31_003]
|
113
177
|
@fp1 = registers[19]
|
114
178
|
@fp2 = registers[20]
|
@@ -116,23 +180,16 @@ module Aurora
|
|
116
180
|
@error = registers[25] & 0x7fff
|
117
181
|
@derated = (41..46).include?(@error)
|
118
182
|
@safe_mode = [47, 48, 49, 72, 74].include?(@error)
|
119
|
-
@blower_only_ecm_speed = registers[340]
|
120
|
-
@aux_heat_ecm_speed = registers[347]
|
121
183
|
@compressor_watts = registers[1147]
|
122
|
-
@blower_watts = registers[1149]
|
123
184
|
@aux_heat_watts = registers[1151]
|
124
|
-
@loop_pump_watts = registers[1165]
|
125
185
|
@total_watts = registers[1153]
|
126
186
|
|
127
|
-
outputs = registers[30]
|
128
187
|
@current_mode = if outputs.include?(:lockout)
|
129
188
|
:lockout
|
130
189
|
elsif registers[362]
|
131
190
|
:dehumidify
|
132
|
-
elsif outputs.include?(:cc2)
|
133
|
-
outputs.include?(:rv) ? :
|
134
|
-
elsif outputs.include?(:cc)
|
135
|
-
outputs.include?(:rv) ? :c1 : :h1
|
191
|
+
elsif outputs.include?(:cc2) || outputs.include?(:cc)
|
192
|
+
outputs.include?(:rv) ? :cooling : :heating
|
136
193
|
elsif outputs.include?(:eh2)
|
137
194
|
outputs.include?(:rv) ? :eh2 : :emergency
|
138
195
|
elsif outputs.include?(:eh1)
|
@@ -145,6 +202,9 @@ module Aurora
|
|
145
202
|
:standby
|
146
203
|
end
|
147
204
|
|
205
|
+
blower.refresh(registers)
|
206
|
+
pump.refresh(registers)
|
207
|
+
|
148
208
|
zones.each do |z|
|
149
209
|
z.refresh(registers)
|
150
210
|
end
|
@@ -172,7 +232,9 @@ module Aurora
|
|
172
232
|
end
|
173
233
|
|
174
234
|
def dhw_setpoint=(value)
|
175
|
-
|
235
|
+
raise ArgumentError unless (100..140).include?(value)
|
236
|
+
|
237
|
+
@modbus_slave.holding_registers[401] = (value * 10).to_i
|
176
238
|
end
|
177
239
|
|
178
240
|
def loop_pressure_trip=(value)
|
@@ -193,6 +255,12 @@ module Aurora
|
|
193
255
|
@modbus_slave.holding_registers[322] = value
|
194
256
|
end
|
195
257
|
|
258
|
+
def line_voltage=(value)
|
259
|
+
raise ArgumentError unless (90..635).include?(value)
|
260
|
+
|
261
|
+
@modbus_slave.holding_registers[112] = value
|
262
|
+
end
|
263
|
+
|
196
264
|
def clear_fault_history
|
197
265
|
@modbus_slave.holding_registers[47] = 0x5555
|
198
266
|
end
|
@@ -224,11 +292,24 @@ module Aurora
|
|
224
292
|
@modbus_slave.holding_registers[323] = pump_speed == :with_compressor ? 0x7fff : pump_speed
|
225
293
|
end
|
226
294
|
|
295
|
+
def energy_monitoring?
|
296
|
+
@energy_monitor == 2
|
297
|
+
end
|
298
|
+
|
299
|
+
def vs_drive?
|
300
|
+
@program == "ABCVSP"
|
301
|
+
end
|
302
|
+
|
303
|
+
def dhw?
|
304
|
+
(-999..999).include?(dhw_water_temperature)
|
305
|
+
end
|
306
|
+
|
227
307
|
# config aurora system
|
228
308
|
{ thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
|
229
309
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
230
310
|
def #{component}?
|
231
|
-
|
311
|
+
return @#{component} if instance_variable_defined?(:@#{component})
|
312
|
+
@#{component} = @modbus_slave.holding_registers[#{register}] != 3
|
232
313
|
end
|
233
314
|
|
234
315
|
def add_#{component}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aurora/component"
|
4
|
+
|
5
|
+
module Aurora
|
6
|
+
module Blower
|
7
|
+
class GenericBlower < Component
|
8
|
+
attr_reader :type, :watts
|
9
|
+
|
10
|
+
def initialize(abc, type)
|
11
|
+
super(abc)
|
12
|
+
@type = type
|
13
|
+
end
|
14
|
+
|
15
|
+
def registers_to_read
|
16
|
+
if abc.energy_monitoring?
|
17
|
+
[1148..1149]
|
18
|
+
else
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def refresh(registers)
|
24
|
+
@watts = registers[1148] if abc.energy_monitoring?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class PSC < GenericBlower
|
29
|
+
attr_reader :running
|
30
|
+
alias running? running
|
31
|
+
|
32
|
+
def refresh(registers)
|
33
|
+
@running = registers[30].include?(:g)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class FiveSpeed < GenericBlower
|
38
|
+
attr_reader :speed
|
39
|
+
|
40
|
+
def speed_range
|
41
|
+
0..4
|
42
|
+
end
|
43
|
+
|
44
|
+
def refresh(registers)
|
45
|
+
outputs = registers[30]
|
46
|
+
@speed = if outputs.include?(:eh1)
|
47
|
+
4
|
48
|
+
elsif outputs.include?(:cc2)
|
49
|
+
3
|
50
|
+
elsif outputs.include?(:cc)
|
51
|
+
2
|
52
|
+
elsif outputs.include?(:g)
|
53
|
+
1
|
54
|
+
else
|
55
|
+
0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ECM < GenericBlower
|
61
|
+
attr_reader :speed, :blower_only_speed, :low_compressor_speed, :high_compressor_speed, :aux_heat_speed,
|
62
|
+
:iz2_desired_speed
|
63
|
+
|
64
|
+
def speed_range
|
65
|
+
0..12
|
66
|
+
end
|
67
|
+
|
68
|
+
def registers_to_read
|
69
|
+
result = super + [340..342, 344, 347]
|
70
|
+
result << 565 if abc.iz2?
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
74
|
+
def refresh(registers)
|
75
|
+
super
|
76
|
+
|
77
|
+
@speed = registers[344]
|
78
|
+
@blower_only_speed = registers[340]
|
79
|
+
@low_compressor_speed = registers[341]
|
80
|
+
@high_compressor_speed = registers[342]
|
81
|
+
@aux_heat_speed = registers[347]
|
82
|
+
@iz2_desired_speed = registers[565] if abc.iz2?
|
83
|
+
end
|
84
|
+
|
85
|
+
{ blower_only: 340,
|
86
|
+
low_compressor: 341,
|
87
|
+
high_compressor: 342,
|
88
|
+
aux_heat: 347 }.each do |(setting, register)|
|
89
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
90
|
+
def #{setting}_speed=(value)
|
91
|
+
raise ArgumentError unless (1..12).include?(value)
|
92
|
+
|
93
|
+
holding_registers[#{register}] = value
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aurora
|
4
|
+
class MockABC
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
def initialize(registers)
|
8
|
+
@registers = registers
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_retry_timeout=(_); end
|
12
|
+
|
13
|
+
def read_retries=(_); end
|
14
|
+
|
15
|
+
def holding_registers
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](*register)
|
20
|
+
if register.length == 1
|
21
|
+
case register.first
|
22
|
+
when Integer
|
23
|
+
@registers[register.first]
|
24
|
+
when Range
|
25
|
+
@registers.values_at(*register.first.to_a)
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Not implemented yet #{register.inspect}"
|
28
|
+
end
|
29
|
+
else
|
30
|
+
read_multiple_holding_registers(*register)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_multiple_holding_registers(*queries)
|
35
|
+
result = {}
|
36
|
+
queries.each do |query|
|
37
|
+
Array(query).each do |i|
|
38
|
+
result[i] = @registers[i]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_holding_register(addr, value)
|
45
|
+
@registers[addr] = value
|
46
|
+
end
|
47
|
+
alias []= write_holding_register
|
48
|
+
end
|
49
|
+
end
|
data/lib/aurora/pump.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aurora/component"
|
4
|
+
|
5
|
+
module Aurora
|
6
|
+
module Pump
|
7
|
+
class GenericPump < Component
|
8
|
+
attr_reader :type, :watts, :waterflow
|
9
|
+
|
10
|
+
def initialize(abc, type)
|
11
|
+
super(abc)
|
12
|
+
@type = type
|
13
|
+
end
|
14
|
+
|
15
|
+
def registers_to_read
|
16
|
+
result = [1117]
|
17
|
+
result.concat([1164..1165]) if abc.energy_monitoring?
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def refresh(registers)
|
22
|
+
@waterflow = registers[1117]
|
23
|
+
@watts = registers[1164] if abc.energy_monitoring?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class VSPump < GenericPump
|
28
|
+
attr_reader :speed, :minimum_speed, :maximum_speed, :manual_control
|
29
|
+
alias manual_control? manual_control
|
30
|
+
|
31
|
+
def registers_to_read
|
32
|
+
super + [321..325]
|
33
|
+
end
|
34
|
+
|
35
|
+
def refresh(registers)
|
36
|
+
super
|
37
|
+
@minimum_speed = registers[321]
|
38
|
+
@maximum_speed = registers[322]
|
39
|
+
@manual_control = registers[323] != :off
|
40
|
+
@speed = registers[325]
|
41
|
+
end
|
42
|
+
|
43
|
+
def manual_control=(value)
|
44
|
+
holding_registers[323] = value ? speed : 0x7fff
|
45
|
+
end
|
46
|
+
|
47
|
+
{ speed: 323,
|
48
|
+
minimum_speed: 321,
|
49
|
+
maximum_speed: 322 }.each do |(setting, register)|
|
50
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
51
|
+
def #{setting}=(value)
|
52
|
+
raise ArgumentError unless (1..100).include?(value)
|
53
|
+
|
54
|
+
holding_registers[#{register}] = value
|
55
|
+
end
|
56
|
+
RUBY
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|