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.
@@ -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
- :fan_speed,
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(modbus_slave)
31
- @modbus_slave = 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
- registers_array = @modbus_slave.holding_registers[105...110]
35
- registers = registers_array.each_with_index.map { |r, i| [i + 105, r] }.to_h
36
- @serial_number = Aurora.transform_registers(registers)[105]
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, 340, 344, 347, 362, 740..741, 900, 1110..1111, 1114, 1147..1153, 1165,
83
- 3027, 31_003]
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
- @fan_speed = registers[344]
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
- @waterflow = registers[1117]
111
- @compressor_speed = registers[3027]
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) ? :c2 : :h2
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
- @modbus_slave.holding_registers[401] = value
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
- @modbus_slave.holding_registers[#{register}] != 3
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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aurora
4
+ class Component
5
+ def initialize(abc)
6
+ @abc = abc
7
+ end
8
+
9
+ private
10
+
11
+ attr_reader :abc
12
+
13
+ def holding_registers
14
+ abc.modbus_slave.holding_registers
15
+ end
16
+ end
17
+ 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
@@ -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