waterfurnace_aurora 0.4.3 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 290c5d09deda24b527dbe0b1a00e93481c0951a38ef0be40b36cfd5fb33a4e4a
4
- data.tar.gz: 33b16f155897683cc0b9a3c915bc3edeb2cb78e869cbfbc7246bd1da01abac71
3
+ metadata.gz: edeaa2444792af1f963829496d445f87a29b5a67efad7e741937ab0f7278b222
4
+ data.tar.gz: 37db08186651a5ee4486a9ee6dceb30f39ae4ca93122ae1c8e357b9b551c2929
5
5
  SHA512:
6
- metadata.gz: 82efe304dfc3a00670c8b80b6c27aa304a0eab0f36ff108b7b1bc046b0d3fdd8c28ca028d7995c8910a8216dccd4e756bb0eadf8efd1c9ff9c0cfbd8a157cab0
7
- data.tar.gz: 47f9d992e4bac6d79bdf514edbf987f48ea367cfd85d9fe8e7d3bcd63396093b7cebb077aaf7d7332c8208926e14ff9b833520bf7b197ce496d2d57de8f80354
6
+ metadata.gz: eed8903a3416f39f393ee1ec77d81a6c3c8c3db14ae3253e22ebf884e1eed624a7869dea9c76ef086a359c657c4f63d20fa94a801622846e6914943e38e0f827
7
+ data.tar.gz: 5e326094aa14976dc8ba432944160c3073d47642bbdba272eca063ea00f6fdf0908f29a563b44f306b5af05754c6c77ae251c4cd225daaea9be8edc2c1101820
data/exe/aurora_monitor CHANGED
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  options.parse!
32
32
 
33
- unless ARGV.length == 2
33
+ unless ARGV.length == 1
34
34
  puts options
35
35
  exit 1
36
36
  end
@@ -39,8 +39,8 @@ uri = URI.parse(ARGV[0])
39
39
 
40
40
  last_registers = {}
41
41
 
42
- SENSOR_REGISTERS = [16, 19, 20, 740, 900, 1109, 1105, 1106, 1107, 1108, 1110, 1111, 1114, 1117, 1134, 1147, 1149, 1151,
43
- 1153, 1165].freeze
42
+ SENSOR_REGISTERS = [16, 19, 20, 740, 900, 901, 903, 908, 909, 1109, 1105, 1106, 1107, 1108, 1110, 1111, 1114, 1117,
43
+ 1134, 1148, 1149, 1150, 1151, 1152, 1153, 1164, 1165, 3326, 3330].freeze
44
44
 
45
45
  io = case uri.scheme
46
46
  when "tcp"
@@ -76,7 +76,11 @@ diff_and_print = lambda do |registers|
76
76
  end
77
77
 
78
78
  server.request_callback = lambda { |uid, func, req|
79
- if func == 68
79
+ if func == 6
80
+ puts "#{Time.now} ===== write register to #{uid}:"
81
+ registers = { req[:addr] => req[:val] }
82
+ puts Aurora.print_registers(registers)
83
+ elsif func == 68
80
84
  puts "#{Time.now} ===== no idea to #{uid}: #{req.inspect}" unless diff_only
81
85
  elsif func == 67
82
86
  puts "#{Time.now} ===== write discontiguous registers to #{uid}:"
@@ -125,7 +129,7 @@ server.response_callback = lambda { |uid, func, res, req|
125
129
  puts "#{Time.now} ===== read discontiguous registers from #{uid}" unless diff_only
126
130
  registers = req.zip(res).to_h
127
131
  diff_and_print.call(registers)
128
- elsif [16, 67, 68].include?(func)
132
+ elsif [6, 16, 67, 68].include?(func)
129
133
  # no output
130
134
  else
131
135
  puts "#{Time.now} **** new func #{func}"
@@ -76,8 +76,10 @@ class MQTTBridge
76
76
  @abc.refresh
77
77
 
78
78
  components = { @homie_abc => @abc,
79
+ @compressor => @abc.compressor,
79
80
  @blower => @abc.blower,
80
- @pump => @abc.pump }
81
+ @pump => @abc.pump,
82
+ @dhw => @abc.dhw }.compact
81
83
  @abc.zones.each_with_index do |z, idx|
82
84
  homie_zone = @homie["zone#{idx + 1}"]
83
85
  components[homie_zone] = z
@@ -89,12 +91,6 @@ class MQTTBridge
89
91
  end
90
92
  end
91
93
 
92
- if @abc.dhw?
93
- @dhw["enabled"].value = @abc.dhw_enabled
94
- @dhw["water-temperature"].value = @abc.dhw_water_temperature
95
- @dhw["set-point"].value = @abc.dhw_setpoint
96
- end
97
-
98
94
  @abc.faults.each_with_index do |fault_count, i|
99
95
  @faults["e#{i + 1}"].value = fault_count
100
96
  end
@@ -109,8 +105,6 @@ class MQTTBridge
109
105
 
110
106
  def publish_basic_attributes
111
107
  @homie_abc = @homie.node("abc", "Aurora Basic Control", "ABC") do |node|
112
- node.property("compressor-speed", "Compressor Speed", :integer, @abc.compressor_speed,
113
- format: @abc.vs_drive? ? 0..12 : 0..2)
114
108
  node.property("current-mode", "Current Heating/Cooling Mode", :enum, @abc.current_mode,
115
109
  format: %w[lockout standby blower heating cooling eh1 eh2 emergency waiting dehumidify])
116
110
  node.property("entering-air-temperature", "Entering Air Temperature", :float, @abc.entering_air_temperature,
@@ -128,13 +122,29 @@ class MQTTBridge
128
122
  format: 0..100)
129
123
  node.property("fp1", "FP1 Sensor", :float, @abc.fp1, unit: "ºF")
130
124
  node.property("fp2", "FP2 Sensor", :float, @abc.fp2, unit: "ºF")
131
- %i[compressor aux_heat total].each do |component|
125
+ %i[aux_heat total].each do |component|
132
126
  component = "#{component}_watts"
133
127
  node.property(component.tr("_", "-"), component.tr("_", " ").titleize, :integer,
134
128
  @abc.public_send(component), unit: "W")
135
129
  end
136
130
  end
137
131
 
132
+ @compressor = @homie.node("compressor", "Compressor", @abc.compressor.type) do |node|
133
+ node.property("speed", "Current compressor speed", :integer, @abc.compressor.speed,
134
+ format: @abc.compressor.speed_range)
135
+ node.property("watts", "Energy Usage", :integer, @abc.compressor.watts, unit: "W") if @abc.energy_monitoring?
136
+
137
+ next unless @abc.compressor.is_a?(Aurora::Compressor::VSDrive)
138
+
139
+ node.property("ambient-temperature", "Compressor ambient temperature", :float,
140
+ @abc.compressor.ambient_temperature, unit: "ºF")
141
+
142
+ next unless @abc.iz2?
143
+
144
+ node.property("iz2-desired-speed", "IZ2 Desired Speed", :integer, @abc.compressor.iz2_desired_speed,
145
+ format: 0..12)
146
+ end
147
+
138
148
  @blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
139
149
  if @abc.blower.respond_to?(:running)
140
150
  node.property("running", "Blower is running", :boolean, @abc.blower.running?)
@@ -154,10 +164,11 @@ class MQTTBridge
154
164
  @mutex.synchronize { property.value = @abc.blower.public_send("#{field}=", value) }
155
165
  end
156
166
  end
157
- if @abc.iz2?
158
- node.property("iz2-desired-speed", "IZ2 Desired Speed", :integer, @abc.blower.iz2_desired_speed,
159
- format: 0..100, unit: "%")
160
- end
167
+
168
+ next unless @abc.iz2?
169
+
170
+ node.property("iz2-desired-speed", "IZ2 Desired Speed", :integer, @abc.blower.iz2_desired_speed,
171
+ format: 0..100, unit: "%")
161
172
  end
162
173
 
163
174
  @pump = @homie.node("pump", "Loop Pump", @abc.pump.type) do |node|
@@ -183,21 +194,24 @@ class MQTTBridge
183
194
  end
184
195
  end
185
196
 
186
- if @abc.dhw?
197
+ if @abc.dhw
187
198
  @dhw = @homie.node("dhw", "Domestic Hot Water Generator", "DHW") do |node|
188
- node.property("enabled", "Enabled", :boolean, @abc.dhw_enabled) do |value, property|
189
- @mutex.synchronize { property.value = @abc.dhw_enabled = value }
199
+ node.property("enabled", "Enabled", :boolean, @abc.dhw.enabled) do |value, property|
200
+ @mutex.synchronize { property.value = @abc.dhw.enabled = value }
190
201
  end
191
- node.property("water-temperature", "DHW Water Temperature", :float,
192
- @abc.dhw_water_temperature, unit: "ºF")
193
- node.property("set-point", "DHW Set Point", :float, @abc.dhw_setpoint, format: 100..140,
194
- unit: "ºF") do |value, property|
195
- @mutex.synchronize { property.value = @abc.dhw_setpoint = value }
202
+ node.property("water-temperature", "Water Temperature", :float,
203
+ @abc.dhw.water_temperature, unit: "ºF")
204
+ node.property("set-point", "Set Point", :float, @abc.dhw.set_point, format: 100..140,
205
+ unit: "ºF") do |value, property|
206
+ @mutex.synchronize { property.value = @abc.dhw.set_point = value }
196
207
  end
197
208
  end
198
209
  end
199
210
 
200
211
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
212
+ node.property("clear-history", "Reset fault counts", :enum, retained: false, format: "clear") do |_|
213
+ @mutex.synchronize { @abc.clear_fault_history }
214
+ end
201
215
  @abc.faults.each_with_index do |count, i|
202
216
  name = Aurora::FAULTS[i + 1]
203
217
  node.property("e#{i + 1}", name || "E#{i + 1}", :integer, count)
@@ -213,25 +227,21 @@ class MQTTBridge
213
227
  format: allowed_modes) do |value, property|
214
228
  @mutex.synchronize { property.value = zone.target_mode = value.to_sym }
215
229
  end
216
- if zone.respond_to?(:current_mode) # TODO: implement for non-IZ2
217
- node.property("current-mode", "Current Heating/Cooling Mode Requested", :enum, zone.current_mode,
218
- format: %w[standby h1 h2 h3 c1 c2])
219
- end
230
+ node.property("current-mode", "Current Heating/Cooling Mode Requested", :enum, zone.current_mode,
231
+ format: %w[standby h1 h2 h3 c1 c2])
220
232
  node.property("target-fan-mode", "Target Fan Mode", :enum, zone.target_fan_mode,
221
233
  format: %w[auto continuous intermittent]) do |value, property|
222
234
  @mutex.synchronize { property.value = zone.target_fan_mode = value.to_sym }
223
235
  end
224
- if zone.respond_to?(:current_fan_mode) # TODO: implement for non-IZ2
225
- node.property("current-fan-mode", "Current Fan Status", :boolean, zone.current_fan_mode)
226
- node.property("fan-intermittent-on", "Fan Intermittent Mode On Duration", :enum, zone.fan_intermittent_on,
227
- unit: "M", format: %w[0 5 10 15 20]) do |value, property|
228
- @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
229
- end
230
- node.property("fan-intermittent-off", "Fan Intermittent Mode Off Duration", :enum, zone.fan_intermittent_on,
231
- unit: "M", format: %w[0 5 10 15 20 25 30 35 40]) do |value, property|
232
- @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
233
- end
236
+ node.property("fan-intermittent-on", "Fan Intermittent Mode On Duration", :enum, zone.fan_intermittent_on,
237
+ unit: "M", format: %w[0 5 10 15 20]) do |value, property|
238
+ @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
239
+ end
240
+ node.property("fan-intermittent-off", "Fan Intermittent Mode Off Duration", :enum, zone.fan_intermittent_on,
241
+ unit: "M", format: %w[0 5 10 15 20 25 30 35 40]) do |value, property|
242
+ @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
234
243
  end
244
+ node.property("current-fan-mode", "Current Fan Status", :boolean, zone.current_fan_mode)
235
245
  if zone.is_a?(Aurora::IZ2Zone)
236
246
  node.property("priority", "Zone Priority", :enum, zone.priority, format: %w[economy comfort])
237
247
  node.property("size", "Size", :enum, zone.size, format: %w[0 25 45 70])
@@ -4,6 +4,8 @@ require "yaml"
4
4
  require "uri"
5
5
 
6
6
  require "aurora/blower"
7
+ require "aurora/compressor"
8
+ require "aurora/dhw"
7
9
  require "aurora/iz2_zone"
8
10
  require "aurora/pump"
9
11
  require "aurora/thermostat"
@@ -40,23 +42,20 @@ module Aurora
40
42
  attr_reader :modbus_slave,
41
43
  :serial_number,
42
44
  :zones,
45
+ :compressor,
43
46
  :blower,
44
47
  :pump,
48
+ :dhw,
45
49
  :faults,
46
50
  :current_mode,
47
- :dhw_enabled,
48
- :dhw_setpoint,
49
51
  :entering_air_temperature,
50
52
  :relative_humidity,
51
53
  :leaving_air_temperature,
52
54
  :leaving_water_temperature,
53
55
  :entering_water_temperature,
54
- :dhw_water_temperature,
55
- :compressor_speed,
56
56
  :outdoor_temperature,
57
57
  :fp1,
58
58
  :fp2,
59
- :compressor_watts,
60
59
  :aux_heat_watts,
61
60
  :total_watts
62
61
 
@@ -68,9 +67,16 @@ module Aurora
68
67
  registers = Aurora.transform_registers(raw_registers.dup)
69
68
  @program = registers[88]
70
69
  @serial_number = registers[105]
71
- @dhw_water_temperature = registers[1114]
72
70
  @energy_monitor = raw_registers[412]
73
71
 
72
+ @zones = if iz2?
73
+ iz2_zone_count = @modbus_slave.holding_registers[483]
74
+ (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
75
+ else
76
+ [Thermostat.new(self)]
77
+ end
78
+
79
+ @compressor = @program == "ABCVSP" ? Compressor::VSDrive.new(self) : Compressor::GenericCompressor.new(self)
74
80
  @blower = case raw_registers[404]
75
81
  when 1, 2 then Blower::ECM.new(self, registers[404])
76
82
  when 3 then Blower::FiveSpeed.new(self, registers[404])
@@ -83,13 +89,8 @@ module Aurora
83
89
  Pump::GenericPump.new(self,
84
90
  registers[413])
85
91
  end
92
+ @dhw = DHW.new(self) if (-999..999).include?(registers[1114])
86
93
 
87
- @zones = if iz2?
88
- iz2_zone_count = @modbus_slave.holding_registers[483]
89
- (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
90
- else
91
- [Thermostat.new(self)]
92
- end
93
94
  @faults = []
94
95
  end
95
96
 
@@ -128,51 +129,32 @@ module Aurora
128
129
  end
129
130
 
130
131
  def refresh
131
- registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114, 1147..1153, 1165,
132
+ registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114, 1150..1153, 1165,
132
133
  31_003]
133
- registers_to_read << (400..401) if dhw?
134
+ zones.each do |z|
135
+ registers_to_read.concat(z.registers_to_read)
136
+ end
137
+ registers_to_read.concat(compressor.registers_to_read)
134
138
  registers_to_read.concat(blower.registers_to_read)
135
139
  registers_to_read.concat(pump.registers_to_read)
136
- registers_to_read.concat([362, 3001]) if vs_drive?
137
-
138
- if zones.first.is_a?(IZ2Zone)
139
- zones.each_with_index do |_z, i|
140
- base1 = 21_203 + i * 9
141
- base2 = 31_007 + i * 3
142
- base3 = 31_200 + i * 3
143
- registers_to_read << (base1..(base1 + 1))
144
- registers_to_read << (base2..(base2 + 2))
145
- registers_to_read << base3
146
- end
147
- else
148
- registers_to_read << 502
149
- registers_to_read << (745..746)
150
- end
140
+ registers_to_read.concat(dhw.registers_to_read) if dhw
141
+ # need dehumidify mode to calculate final current mode;
142
+ # apparently non-VSD doesn't have this register at all?
143
+ registers_to_read.concat([362]) if compressor.is_a?(Compressor::VSDrive)
151
144
 
152
- @faults = @modbus_slave.holding_registers[601..699]
145
+ faults = @modbus_slave.read_multiple_holding_registers(601..699)
146
+ @faults = Aurora.transform_registers(faults).values
153
147
 
154
148
  registers = @modbus_slave.holding_registers[*registers_to_read]
155
149
  Aurora.transform_registers(registers)
156
150
 
157
151
  outputs = registers[30]
158
152
 
159
- @dhw_enabled = registers[400]
160
- @dhw_setpoint = registers[401]
161
153
  @entering_air_temperature = registers[740]
162
154
  @relative_humidity = registers[741]
163
155
  @leaving_air_temperature = registers[900]
164
156
  @leaving_water_temperature = registers[1110]
165
157
  @entering_water_temperature = registers[1111]
166
- @dhw_water_temperature = registers[1114]
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
176
158
  @outdoor_temperature = registers[31_003]
177
159
  @fp1 = registers[19]
178
160
  @fp2 = registers[20]
@@ -180,7 +162,6 @@ module Aurora
180
162
  @error = registers[25] & 0x7fff
181
163
  @derated = (41..46).include?(@error)
182
164
  @safe_mode = [47, 48, 49, 72, 74].include?(@error)
183
- @compressor_watts = registers[1147]
184
165
  @aux_heat_watts = registers[1151]
185
166
  @total_watts = registers[1153]
186
167
 
@@ -202,24 +183,13 @@ module Aurora
202
183
  :standby
203
184
  end
204
185
 
205
- blower.refresh(registers)
206
- pump.refresh(registers)
207
-
208
186
  zones.each do |z|
209
187
  z.refresh(registers)
210
188
  end
211
- end
212
-
213
- def blower_only_ecm_speed=(value)
214
- return unless (1..12).include?(value)
215
-
216
- @modbus_slave.holding_registers[340] = value
217
- end
218
-
219
- def aux_heat_ecm_speed=(value)
220
- return unless (1..12).include?(value)
221
-
222
- @modbus_slave.holding_registers[347] = value
189
+ compressor.refresh(registers)
190
+ blower.refresh(registers)
191
+ pump.refresh(registers)
192
+ dhw&.refresh(registers)
223
193
  end
224
194
 
225
195
  def cooling_airflow_adjustment=(value)
@@ -227,34 +197,10 @@ module Aurora
227
197
  @modbus_slave.holding_registers[346] = value
228
198
  end
229
199
 
230
- def dhw_enabled=(value)
231
- @modbus_slave.holding_registers[400] = value ? 1 : 0
232
- end
233
-
234
- def dhw_setpoint=(value)
235
- raise ArgumentError unless (100..140).include?(value)
236
-
237
- @modbus_slave.holding_registers[401] = (value * 10).to_i
238
- end
239
-
240
200
  def loop_pressure_trip=(value)
241
201
  @modbus_slave.holding_registers[419] = (value * 10).to_i
242
202
  end
243
203
 
244
- def vs_pump_control=(value)
245
- raise ArgumentError unless (value = VS_PUMP_CONTROL.invert[value])
246
-
247
- @modbus_slave.holding_registers[323] = value
248
- end
249
-
250
- def vs_pump_min=(value)
251
- @modbus_slave.holding_registers[321] = value
252
- end
253
-
254
- def vs_pump_max=(value)
255
- @modbus_slave.holding_registers[322] = value
256
- end
257
-
258
204
  def line_voltage=(value)
259
205
  raise ArgumentError unless (90..635).include?(value)
260
206
 
@@ -296,14 +242,6 @@ module Aurora
296
242
  @energy_monitor == 2
297
243
  end
298
244
 
299
- def vs_drive?
300
- @program == "ABCVSP"
301
- end
302
-
303
- def dhw?
304
- (-999..999).include?(dhw_water_temperature)
305
- end
306
-
307
245
  # config aurora system
308
246
  { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
309
247
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -6,6 +6,12 @@ module Aurora
6
6
  @abc = abc
7
7
  end
8
8
 
9
+ def inspect
10
+ "#<Aurora::#{self.class.name} #{(instance_variables - [:@abc]).map do |iv|
11
+ "#{iv}=#{instance_variable_get(iv).inspect}"
12
+ end.join(', ')}>"
13
+ end
14
+
9
15
  private
10
16
 
11
17
  attr_reader :abc
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aurora/component"
4
+
5
+ module Aurora
6
+ module Compressor
7
+ class GenericCompressor < Component
8
+ attr_reader :speed, :watts
9
+
10
+ def type
11
+ "Compressor"
12
+ end
13
+
14
+ def speed_range
15
+ 0..2
16
+ end
17
+
18
+ def registers_to_read
19
+ if abc.energy_monitoring?
20
+ [1146..1147]
21
+ else
22
+ []
23
+ end
24
+ end
25
+
26
+ def refresh(registers)
27
+ outputs = registers[30]
28
+ @speed = if outputs.include?(:cc2)
29
+ 2
30
+ elsif outputs.include?(:cc)
31
+ 1
32
+ else
33
+ 0
34
+ end
35
+ @watts = registers[1146] if abc.energy_monitoring?
36
+ end
37
+ end
38
+
39
+ class VSDrive < GenericCompressor
40
+ attr_reader :ambient_temperature, :iz2_desired_speed
41
+
42
+ def type
43
+ "Variable Speed Drive"
44
+ end
45
+
46
+ def speed_range
47
+ 0..12
48
+ end
49
+
50
+ def registers_to_read
51
+ result = super + [209, 3001, 3326]
52
+ result << 564 if abc.iz2?
53
+ result
54
+ end
55
+
56
+ def refresh(registers)
57
+ super
58
+
59
+ @speed = registers[3001]
60
+ @ambient_temperature = registers[3326]
61
+ @iz2_desired_speed = registers[564] if abc.iz2?
62
+ end
63
+ end
64
+ end
65
+ end
data/lib/aurora/dhw.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aurora/component"
4
+
5
+ module Aurora
6
+ class DHW < Component
7
+ attr_reader :enabled, :set_point, :water_temperature
8
+
9
+ def registers_to_read
10
+ [400..401, 1114]
11
+ end
12
+
13
+ def refresh(registers)
14
+ @enabled = registers[400]
15
+ @set_point = registers[401]
16
+ @water_temperature = registers[1114]
17
+ end
18
+
19
+ def enabled=(value)
20
+ holding_registers[400] = value ? 1 : 0
21
+ end
22
+
23
+ def set_point=(value) # rubocop:disable Naming/AccessorMethodName
24
+ raise ArgumentError unless (100..140).include?(value)
25
+
26
+ raw_value = (value * 10).to_i
27
+ holding_registers[401] = raw_value
28
+ @set_point = value
29
+ end
30
+ end
31
+ end
@@ -5,18 +5,22 @@ require "aurora/thermostat"
5
5
  module Aurora
6
6
  class IZ2Zone < Thermostat
7
7
  attr_reader :zone_number,
8
- :current_mode,
9
- :current_fan_mode,
10
- :fan_intermittent_on,
11
- :fan_intermittent_off,
12
8
  :priority,
13
- :size, :normalized_size
9
+ :size,
10
+ :normalized_size
14
11
 
15
12
  def initialize(abc, zone_number)
16
13
  super(abc)
17
14
  @zone_number = zone_number
18
15
  end
19
16
 
17
+ def registers_to_read
18
+ base1 = 21_203 + (zone_number - 1) * 9
19
+ base2 = 31_007 + (zone_number - 1) * 3
20
+ base3 = 31_200 + (zone_number - 1) * 3
21
+ [base1..(base1 + 1), base2..(base2 + 2), base3]
22
+ end
23
+
20
24
  def refresh(registers)
21
25
  @ambient_temperature = registers[31_007 + (zone_number - 1) * 3]
22
26
 
@@ -41,28 +45,28 @@ module Aurora
41
45
  def target_mode=(value)
42
46
  return unless (raw_value = Aurora::HEATING_MODE.invert[value])
43
47
 
44
- @abc.modbus_slave.holding_registers[21_202 + (zone_number - 1) * 9] = raw_value
48
+ holding_registers[21_202 + (zone_number - 1) * 9] = raw_value
45
49
  @target_mode = value
46
50
  end
47
51
 
48
52
  def target_fan_mode=(value)
49
53
  return unless (raw_value = Aurora::FAN_MODE.invert[value])
50
54
 
51
- @abc.modbus_slave.holding_registers[21_205 + (zone_number - 1) * 9] = raw_value
55
+ holding_registers[21_205 + (zone_number - 1) * 9] = raw_value
52
56
  @target_fan_mode = value
53
57
  end
54
58
 
55
59
  def fan_intermittent_on=(value)
56
60
  return unless value >= 0 && value <= 25 && (value % 5).zero?
57
61
 
58
- @abc.modbus_slave.holding_registers[21_206 + (zone_number - 1) * 9] = value
62
+ holding_registers[21_206 + (zone_number - 1) * 9] = value
59
63
  @fan_intermittent_on = value
60
64
  end
61
65
 
62
66
  def fan_intermittent_off=(value)
63
67
  return unless value >= 0 && value <= 40 && (value % 5).zero?
64
68
 
65
- @abc.modbus_slave.holding_registers[21_207 + (zone_number - 1) * 9] = value
69
+ holding_registers[21_207 + (zone_number - 1) * 9] = value
66
70
  @fan_intermittent_off = value
67
71
  end
68
72
 
@@ -70,7 +74,7 @@ module Aurora
70
74
  return unless value >= 40 && value <= 90
71
75
 
72
76
  raw_value = (value * 10).to_i
73
- @abc.modbus_slave.holding_registers[21_203 + (zone_number - 1) * 9] = raw_value
77
+ holding_registers[21_203 + (zone_number - 1) * 9] = raw_value
74
78
  @heating_target_temperature = value
75
79
  end
76
80
 
@@ -78,8 +82,8 @@ module Aurora
78
82
  return unless value >= 54 && value <= 99
79
83
 
80
84
  raw_value = (value * 10).to_i
81
- @abc.modbus_slave.holding_registers[21_204 + (zone_number - 1) * 9] = value
82
- @cooling_target_temperature = raw_value
85
+ holding_registers[21_204 + (zone_number - 1) * 9] = raw_value
86
+ @cooling_target_temperature = value
83
87
  end
84
88
  end
85
89
  end
@@ -420,6 +420,15 @@ module Aurora
420
420
  end
421
421
  end
422
422
 
423
+ def thermostat_configuration2(value)
424
+ result = {
425
+ mode: HEATING_MODE[(value >> 8) & 0x07]
426
+ }
427
+ leftover = value & ~0x0700
428
+ result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
429
+ result
430
+ end
431
+
423
432
  def zone_configuration1(value)
424
433
  fan = if value & 0x80 == 0x80
425
434
  :continuous
@@ -433,7 +442,7 @@ module Aurora
433
442
  on_time: ((value >> 9) & 0x7) * 5,
434
443
  off_time: (((value >> 12) & 0x7) + 1) * 5,
435
444
  cooling_target_temperature: ((value & 0x7e) >> 1) + 36,
436
- heating_target_temperature_carry: value & 0o1
445
+ heating_target_temperature_carry: value & 0x01
437
446
  }
438
447
  leftover = value & ~0x7fff
439
448
  result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
@@ -476,7 +485,7 @@ module Aurora
476
485
  REGISTER_CONVERTERS = {
477
486
  TO_HUNDREDTHS => [2, 3, 417, 418, 801, 804, 807, 813, 816, 817, 819, 820, 825, 828],
478
487
  method(:dipswitch_settings) => [4, 33],
479
- TO_TENTHS => [19, 20, 401, 419, 501, 502, 567, 740, 745, 746, 747, 900,
488
+ TO_TENTHS => [19, 20, 401, 419, 501, 502, 567, 740, 745, 746, 747, 900, 901, 903,
480
489
  1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1119, 1124, 1125, 1134,
481
490
  3322, 3323, 3325, 3326, 3327, 3330, 3522, 3903, 3905, 3906,
482
491
  12_619, 12_620,
@@ -503,7 +512,7 @@ module Aurora
503
512
  ->(v) { from_bitmask(v, VS_ALARM2) } => [218, 3227],
504
513
  ->(v) { from_bitmask(v, VS_EEV2) } => [280, 3804],
505
514
  method(:vs_manual_control) => [323],
506
- NEGATABLE => [346, 1146],
515
+ NEGATABLE => [346],
507
516
  ->(v) { BRINE_TYPE[v] } => [402],
508
517
  ->(v) { FLOW_METER_TYPE[v] } => [403],
509
518
  ->(v) { BLOWER_TYPE[v] } => [404],
@@ -514,6 +523,7 @@ module Aurora
514
523
  ->(v) { PUMP_TYPE[v] } => [413],
515
524
  ->(v) { PHASE_TYPE[v] } => [416],
516
525
  method(:iz2_fan_desired) => [565],
526
+ ->(v) { v == 0xffff ? 0 : v } => 601..699,
517
527
  ->(registers, idx) { to_string(registers, idx, 8) } => [710],
518
528
  ->(v) { COMPONENT_STATUS[v] } => [800, 803, 806, 812, 815, 818, 824, 827],
519
529
  method(:axb_inputs) => [1103],
@@ -521,12 +531,13 @@ module Aurora
521
531
  ->(v) { TO_TENTHS.call(NEGATABLE.call(v)) } => [1135, 1136],
522
532
  method(:to_int32) => [1146, 1148, 1150, 1152, 1154, 1156, 1164, 3422, 3424],
523
533
  method(:manual_operation) => [3002],
534
+ method(:thermostat_configuration2) => [12_006],
524
535
  ->(v) { HEATING_MODE[v] } => [12_606, 21_202, 21_211, 21_220, 21_229, 21_238, 21_247],
525
536
  ->(v) { FAN_MODE[v] } => [12_621, 21_205, 21_214, 21_223, 21_232, 21_241, 21_250],
526
537
  ->(v) { from_bitmask(v, HUMIDIFIER_SETTINGS) } => [31_109],
527
538
  ->(v) { { humidification_target: v >> 8, dehumidification_target: v & 0xff } } => [31_110],
528
539
  method(:iz2_demand) => [31_005],
529
- method(:zone_configuration1) => [31_008, 31_011, 31_014, 31_017, 31_020, 31_023],
540
+ method(:zone_configuration1) => [12_005, 31_008, 31_011, 31_014, 31_017, 31_020, 31_023],
530
541
  method(:zone_configuration2) => [31_009, 31_012, 31_015, 31_018, 31_021, 31_024],
531
542
  method(:zone_configuration3) => [31_200, 31_203, 31_206, 31_209, 31_212, 31_215],
532
543
  ->(registers, idx) { to_string(registers, idx, 13) } => [31_400],
@@ -540,7 +551,7 @@ module Aurora
540
551
  REGISTER_FORMATS = {
541
552
  "%ds" => [1, 6, 9, 15, 84, 85, 110],
542
553
  "%dV" => [16, 112, 3331, 3424, 3523],
543
- "%0.1fºF" => [19, 20, 401, 501, 502, 567, 740, 745, 746, 747, 900,
554
+ "%0.1fºF" => [19, 20, 401, 501, 502, 567, 740, 745, 746, 747, 900, 903,
544
555
  1109, 1110, 1111, 1112, 1113, 1114, 1124, 1125, 1134, 1135, 1136,
545
556
  3325, 3326, 3327, 3330, 3522, 3903, 3905, 3906,
546
557
  12_619, 12_620,
@@ -553,8 +564,8 @@ module Aurora
553
564
  31_003,
554
565
  31_007, 31_010, 31_013, 31_016, 31_019, 31_022],
555
566
  "E%d" => [25, 26],
556
- "%d%%" => [282, 321, 322, 325, 346, 565, 741, 1126, 3332, 3524, 3808],
557
- "%0.1f psi" => [419, 1115, 1116, 1119, 3322, 3323],
567
+ "%d%%" => [282, 321, 322, 325, 346, 565, 741, 908, 1126, 3332, 3524, 3808],
568
+ "%0.1f psi" => [419, 901, 1115, 1116, 1119, 3322, 3323],
558
569
  "%0.1fA" => [1105, 1106, 1107, 1108],
559
570
  "%0.1fgpm" => [1117],
560
571
  "%dW" => [1146, 1148, 1150, 1152, 1164, 3422],
@@ -812,6 +823,10 @@ module Aurora
812
823
  828 => "AWL Version",
813
824
  829 => "AWL Revision",
814
825
  900 => "Leaving Air",
826
+ 901 => "Suction Pressure",
827
+ 903 => "SuperHeat Temperature",
828
+ 908 => "EEV Open %",
829
+ 909 => "SubCooling (Cooling)",
815
830
  1103 => "AXB Inputs",
816
831
  1104 => "AXB Outputs",
817
832
  1105 => "Blower Amps",
@@ -841,10 +856,6 @@ module Aurora
841
856
  1154 => "Heat of Extraction",
842
857
  1156 => "Heat of Rejection",
843
858
  1164 => "Pump Watts",
844
- 12_606 => "Heating Mode (write)",
845
- 12_619 => "Heating Setpoint (write)",
846
- 12_620 => "Cooling Setpoint (write)",
847
- 12_621 => "Fan Mode (write)",
848
859
  3000 => "Compressor Speed Desired",
849
860
  3001 => "Compressor Speed Actual",
850
861
  3002 => "Manual Operation",
@@ -876,6 +887,14 @@ module Aurora
876
887
  3904 => "VS Drive Leaving Air Temperature?",
877
888
  3905 => "VS Drive Saturated Evaporator Discharge Temperature",
878
889
  3906 => "VS Drive SuperHeat Temperature",
890
+ 12_005 => "Fan Configuration",
891
+ 12_006 => "Heating Mode",
892
+ 12_606 => "Heating Mode (write)",
893
+ 12_619 => "Heating Setpoint (write)",
894
+ 12_620 => "Cooling Setpoint (write)",
895
+ 12_621 => "Fan Mode (write)",
896
+ 12_622 => "Intermittent Fan On Time (write)",
897
+ 12_623 => "Intermittent Fan Off Time (write)",
879
898
  31_003 => "Outdoor Temp",
880
899
  31_005 => "IZ2 Demand",
881
900
  31_109 => "Humidifier Mode", # write to 21114
@@ -5,15 +5,39 @@ require "aurora/component"
5
5
  module Aurora
6
6
  class Thermostat < Component
7
7
  attr_reader :target_mode,
8
+ :current_mode,
8
9
  :target_fan_mode,
10
+ :current_fan_mode,
9
11
  :ambient_temperature,
10
12
  :cooling_target_temperature,
11
- :heating_target_temperature
13
+ :heating_target_temperature,
14
+ :fan_intermittent_on,
15
+ :fan_intermittent_off
16
+
17
+ def registers_to_read
18
+ [31, 502, 745..746, 12_005..12_006]
19
+ end
12
20
 
13
21
  def refresh(registers)
14
22
  @ambient_temperature = registers[502]
15
- @heating_target_temperature = registers[746]
16
- @cooling_target_temperature = registers[745]
23
+ @heating_target_temperature = registers[745]
24
+ @cooling_target_temperature = registers[746]
25
+ config1 = registers[12_005]
26
+ config2 = registers[12_006]
27
+ @target_fan_mode = config1[:fan]
28
+ @fan_intermittent_on = config1[:on_time]
29
+ @fan_intermittent_off = config1[:off_time]
30
+ @target_mode = config2[:mode]
31
+
32
+ inputs = registers[31]
33
+ @current_fan_mode = inputs.include?(:g)
34
+ @current_mode = if inputs[:y2]
35
+ inputs[:o] ? :c2 : :h2
36
+ elsif inputs[:y1]
37
+ inputs[:o] ? :c1 : :h1
38
+ else
39
+ :standby
40
+ end
17
41
  end
18
42
 
19
43
  def target_mode=(value)
@@ -46,10 +70,18 @@ module Aurora
46
70
  @cooling_target_temperature = value
47
71
  end
48
72
 
49
- def inspect
50
- "#<Aurora::#{self.class.name} #{(instance_variables - [:@abc]).map do |iv|
51
- "#{iv}=#{instance_variable_get(iv).inspect}"
52
- end.join(', ')}>"
73
+ def fan_intermittent_on=(value)
74
+ return unless value >= 0 && value <= 25 && (value % 5).zero?
75
+
76
+ holding_registers[12_622] = value
77
+ @fan_intermittent_on = value
78
+ end
79
+
80
+ def fan_intermittent_off=(value)
81
+ return unless value >= 0 && value <= 40 && (value % 5).zero?
82
+
83
+ holding_registers[12_623] = value
84
+ @fan_intermittent_off = value
53
85
  end
54
86
  end
55
87
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aurora
4
- VERSION = "0.4.3"
4
+ VERSION = "0.5.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waterfurnace_aurora
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-31 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ccutrer-serialport
@@ -136,7 +136,9 @@ files:
136
136
  - lib/aurora/abc_client.rb
137
137
  - lib/aurora/blower.rb
138
138
  - lib/aurora/component.rb
139
+ - lib/aurora/compressor.rb
139
140
  - lib/aurora/core_ext/string.rb
141
+ - lib/aurora/dhw.rb
140
142
  - lib/aurora/iz2_zone.rb
141
143
  - lib/aurora/mock_abc.rb
142
144
  - lib/aurora/modbus/server.rb