waterfurnace_aurora 0.7.5 → 1.0.0

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: 037ee3ea9f7274fbc12400840c5f174a70e42361c686753728b31ef8c4c6faaf
4
- data.tar.gz: 4ce5ddc17e4212d8a73f1c2f9255608d9d9a933da8d74e84af3552acd8bf7021
3
+ metadata.gz: 5a92e9aadd14ee0154443cea52907f793880242ce22a44f3df7912065074c3c8
4
+ data.tar.gz: 553a945493652a50f92f49e4e0620db8e03305c3ff0ab65823f20c15db2ac89d
5
5
  SHA512:
6
- metadata.gz: d21f026be0e2d578b0a12064b5f90c71e039636d6fa7c21cb2a30c87fae2f2a0cc9eb9ffc65ca025cf415800abab3c32185a52ecc1e86ed5c15b93fe9516391c
7
- data.tar.gz: 2dc36c52c465c776f95e6581f60c20816b06c76d392d485c5c14b7c85a82c16b141b68da569eda8bfce1ddfed60bf1027166951b9bbf17383fe8f410506c3e39
6
+ metadata.gz: 5fee6734f5e5752aec571c2c93a81e79be950af8c8f4a3ab3259f7d6b1d5e2540d4fd616be06d911a382c710b23ed43c1b1ecf23db23444022c24216faff52cb
7
+ data.tar.gz: c2e5a9f54f7bd0f6b869ffff94b4cd0a0bf9de65ea29d8342fe690f582893a9fe0a7b0f0ce94ba4c0ecf6595156d21a12e7e8203023aaadc923a56c0951f817f
data/exe/aurora_fetch CHANGED
@@ -9,11 +9,19 @@ require "uri"
9
9
  require "yaml"
10
10
 
11
11
  debug_modbus = yaml = false
12
+ try_individual = nil
12
13
 
13
14
  options = OptionParser.new do |opts|
14
15
  opts.banner = "Usage: aurora_fetch /path/to/serial/port REGISTERS [options]"
15
16
 
17
+ opts.separator("")
18
+ opts.separator("Use `known` to fetch all identified registers. Use `valid` to fetch all registers that will respond.")
19
+ opts.separator("")
20
+
16
21
  opts.on("--debug-modbus", "Print actual protocol bytes") { debug_modbus = true }
22
+ opts.on("--[no-]try-individual",
23
+ "Query registers one-by-one if a range has an illegal address. " \
24
+ "Defaults to true for `valid` and `known` special registers, false otherwise.") { |v| try_individual = v }
17
25
  opts.on("-y", "--yaml", "Output raw values as YAML") { yaml = true }
18
26
  opts.on("-v", "--version", "Print version") do
19
27
  puts Aurora::VERSION
@@ -32,11 +40,13 @@ unless ARGV.length == 2
32
40
  exit 1
33
41
  end
34
42
 
35
- abc = Aurora::ABCClient.new(ARGV[0])
36
- abc.modbus_slave.logger = Logger.new($stdout)
37
- abc.modbus_slave.logger.level = debug_modbus ? :debug : :warn
43
+ modbus_slave = Aurora::ABCClient.open_modbus_slave(ARGV[0])
44
+ modbus_slave.read_retry_timeout = 15
45
+ modbus_slave.read_retries = 2
46
+ modbus_slave.logger = Logger.new($stdout)
47
+ modbus_slave.logger.level = debug_modbus ? :debug : :warn
38
48
 
39
- registers = abc.query_registers(ARGV[1])
49
+ registers = Aurora::ABCClient.query_registers(modbus_slave, ARGV[1], try_individual: try_individual)
40
50
 
41
51
  if yaml
42
52
  puts YAML.dump(registers)
@@ -93,6 +93,7 @@ class MQTTBridge
93
93
  @abc.refresh
94
94
 
95
95
  components = { @homie_abc => @abc,
96
+ @aux_heat => @abc.aux_heat,
96
97
  @compressor => @abc.compressor,
97
98
  @blower => @abc.blower,
98
99
  @pump => @abc.pump,
@@ -131,7 +132,7 @@ class MQTTBridge
131
132
  }
132
133
 
133
134
  @homie_abc = @homie.node("abc", "Heat Pump", "ABC") do |node|
134
- allowed_modes = %w[lockout standby blower heating cooling eh1 eh2 emergency waiting]
135
+ allowed_modes = %w[lockout standby blower heating cooling waiting]
135
136
  allowed_modes << "dehumidify" if @abc.compressor.is_a?(Aurora::Compressor::VSDrive)
136
137
  node.property("current-mode",
137
138
  "Current Heating/Cooling Mode",
@@ -155,14 +156,16 @@ class MQTTBridge
155
156
  hass: { sensor: { device_class: :temperature,
156
157
  state_class: :measurement,
157
158
  entity_category: :diagnostic } })
158
- node.property("leaving-air-temperature",
159
- "Leaving Air Temperature",
160
- :float,
161
- @abc.leaving_air_temperature,
162
- unit: "°F",
163
- hass: { sensor: { device_class: :temperature,
164
- state_class: :measurement,
165
- entity_category: :diagnostic } })
159
+ if @abc.awl_communicating?
160
+ node.property("leaving-air-temperature",
161
+ "Leaving Air Temperature",
162
+ :float,
163
+ @abc.leaving_air_temperature,
164
+ unit: "°F",
165
+ hass: { sensor: { device_class: :temperature,
166
+ state_class: :measurement,
167
+ entity_category: :diagnostic } })
168
+ end
166
169
  node.property("leaving-water-temperature",
167
170
  "Leaving Water Temperature",
168
171
  :float,
@@ -171,7 +174,8 @@ class MQTTBridge
171
174
  hass: { sensor: { device_class: :temperature,
172
175
  state_class: :measurement,
173
176
  entity_category: :diagnostic } })
174
- unless @abc.outdoor_temperature.zero? # TODO: figure out the config if this actually exists
177
+ # TODO: figure out the config if this actually exists
178
+ if @abc.awl_communicating? && !@abc.outdoor_temperature.zero?
175
179
  node.property("outdoor-temperature",
176
180
  "Outdoor Temperature",
177
181
  :float,
@@ -181,6 +185,14 @@ class MQTTBridge
181
185
  state_class: :measurement } })
182
186
  end
183
187
 
188
+ node.property("line-voltage",
189
+ "Line Voltage Setting",
190
+ :integer,
191
+ @abc.line_voltage,
192
+ format: 90..635,
193
+ unit: "V") do |value, property|
194
+ @mutex.synchronize { property.value = @abc.line_voltage = value }
195
+ end
184
196
  node.property("fp1",
185
197
  "FP1 Sensor",
186
198
  :float,
@@ -197,12 +209,11 @@ class MQTTBridge
197
209
  hass: { sensor: { device_class: :temperature,
198
210
  state_class: :measurement,
199
211
  entity_category: :diagnostic } })
200
- %i[aux_heat total].each do |component|
201
- component = "#{component}_watts"
202
- node.property(component.tr("_", "-"),
203
- component.sub("_watts", " Power Usage").tr("_", " ").titleize,
212
+ if @abc.energy_monitoring?
213
+ node.property("watts",
214
+ "Total Power Usage",
204
215
  :integer,
205
- @abc.public_send(component),
216
+ @abc.watts,
206
217
  unit: "W",
207
218
  hass: { sensor: { device_class: :power,
208
219
  state_class: :measurement } })
@@ -236,6 +247,30 @@ class MQTTBridge
236
247
  hass: { sensor: { device_class: :temperature,
237
248
  state_class: :measurement,
238
249
  entity_category: :diagnostic } })
250
+ node.property("drive-temperature",
251
+ "Drive Temperature",
252
+ :float,
253
+ @abc.compressor.drive_temperature,
254
+ unit: "°F",
255
+ hass: { sensor: { device_class: :temperature,
256
+ state_class: :measurement,
257
+ entity_category: :diagnostic } })
258
+ node.property("inverter-temperature",
259
+ "Inverter Temperature",
260
+ :float,
261
+ @abc.compressor.inverter_temperature,
262
+ unit: "°F",
263
+ hass: { sensor: { device_class: :temperature,
264
+ state_class: :measurement,
265
+ entity_category: :diagnostic } })
266
+ node.property("fan-speed",
267
+ "Fan Speed",
268
+ :integer,
269
+ @abc.compressor.fan_speed,
270
+ unit: "%",
271
+ format: 0..100,
272
+ hass: { sensor: { state_class: :measurement,
273
+ entity_category: :diagnostic } })
239
274
 
240
275
  next unless @abc.iz2?
241
276
 
@@ -248,6 +283,25 @@ class MQTTBridge
248
283
  entity_category: :diagnostic } })
249
284
  end
250
285
 
286
+ @aux_heat = @homie.node("aux-heat", "Aux Heater", "Aux Heater") do |node|
287
+ node.property("stage",
288
+ "Current Stage",
289
+ :integer,
290
+ @abc.aux_heat.stage,
291
+ format: 0..2,
292
+ hass: { sensor: { state_class: :measurement } })
293
+
294
+ if @abc.energy_monitoring?
295
+ node.property("watts",
296
+ "Power Usage",
297
+ :integer,
298
+ @abc.aux_heat.watts,
299
+ unit: "W",
300
+ hass: { sensor: { device_class: :power,
301
+ state_class: :measurement } })
302
+ end
303
+ end
304
+
251
305
  @blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
252
306
  node.property("running",
253
307
  "Running",
@@ -325,15 +379,6 @@ class MQTTBridge
325
379
  :boolean,
326
380
  @abc.pump.running,
327
381
  hass: { binary_sensor: { device_class: :running } })
328
- node.property("speed",
329
- "Speed",
330
- :integer,
331
- @abc.pump.speed,
332
- format: 0..100,
333
- unit: "%",
334
- hass: { number: { entity_category: :diagnostic } }) do |value, property|
335
- @mutex.synchronize { property.value = @abc.pump.speed = value }
336
- end
337
382
  node.property("manual-control",
338
383
  "Manual Control",
339
384
  :boolean,
@@ -359,6 +404,17 @@ class MQTTBridge
359
404
  hass: { number: { entity_category: :config } }) do |value, property|
360
405
  @mutex.synchronize { property.value = @abc.pump.maximum_speed = value }
361
406
  end
407
+ next unless @abc.awl_axb?
408
+
409
+ node.property("speed",
410
+ "Speed",
411
+ :integer,
412
+ @abc.pump.speed,
413
+ format: 0..100,
414
+ unit: "%",
415
+ hass: { number: { entity_category: :diagnostic } }) do |value, property|
416
+ @mutex.synchronize { property.value = @abc.pump.speed = value }
417
+ end
362
418
  end
363
419
 
364
420
  if @abc.dhw
@@ -388,47 +444,41 @@ class MQTTBridge
388
444
  @abc.dhw.set_point,
389
445
  format: 100..140,
390
446
  unit: "°F",
391
- hass: :number) do |value, property|
447
+ hass: { number: { step: 5 } }) do |value, property|
392
448
  @mutex.synchronize { property.value = @abc.dhw.set_point = value }
393
449
  end
394
450
  end
395
451
  end
396
452
 
397
453
  @humidistat = @homie.node("humidistat", "Humidistat", "Humidistat") do |node|
398
- node.property("relative-humidity",
399
- "Relative Humidity",
400
- :integer,
401
- @abc.humidistat.relative_humidity,
402
- unit: "%",
403
- format: 0..100,
404
- hass: { sensor: { device_class: :humidity,
405
- state_class: :measurement } })
406
454
  if @abc.humidistat.humidifier?
407
455
  node.property("humidifier-running",
408
456
  "Humidifier Running",
409
457
  :boolean,
410
458
  @abc.humidistat.humidifier_running?)
411
- node.property("humidifier-mode",
412
- "Humidifier Mode",
413
- :enum,
414
- @abc.humidistat.humidifier_mode,
415
- format: %i[auto manual]) do |value, property|
416
- @mutex.synchronize { property.value = @abc.humidistat.humidifier_mode = value.to_sym }
417
- end
418
- node.property("humidification-target",
419
- "Humidification Target Relative Humidity",
420
- :integer,
421
- @abc.humidistat.humidification_target,
422
- unit: "%",
423
- format: 15..50) do |value, property|
424
- @mutex.synchronize { property.value = @abc.humidistat.humidification_target = value }
459
+ if @abc.awl_communicating?
460
+ node.property("humidifier-mode",
461
+ "Humidifier Mode",
462
+ :enum,
463
+ @abc.humidistat.humidifier_mode,
464
+ format: %i[auto manual]) do |value, property|
465
+ @mutex.synchronize { property.value = @abc.humidistat.humidifier_mode = value.to_sym }
466
+ end
467
+ node.property("humidification-target",
468
+ "Humidification Target Relative Humidity",
469
+ :integer,
470
+ @abc.humidistat.humidification_target,
471
+ unit: "%",
472
+ format: 15..50) do |value, property|
473
+ @mutex.synchronize { property.value = @abc.humidistat.humidification_target = value }
474
+ end
475
+ node.hass_humidifier("humidifier-running",
476
+ target_property: "humidification-target",
477
+ mode_property: "humidifier-mode",
478
+ id: "humidifier",
479
+ name: "Humidifier",
480
+ device_class: :humidifier)
425
481
  end
426
- node.hass_humidifier("humidifier-running",
427
- target_property: "humidification-target",
428
- mode_property: "humidifier-mode",
429
- id: "humidifier",
430
- name: "Humidifier",
431
- device_class: :humidifier)
432
482
  end
433
483
 
434
484
  # VSDrive can perform active dehumidification, even without a dedicated dehumidifier
@@ -438,28 +488,40 @@ class MQTTBridge
438
488
  "Dehumidifier Running",
439
489
  :boolean,
440
490
  @abc.humidistat.dehumidifier_running?)
441
- node.property("dehumidifier-mode",
442
- "Dehumidifier Mode",
443
- :enum,
444
- @abc.humidistat.dehumidifier_mode,
445
- format: %i[auto manual]) do |value, property|
446
- @mutex.synchronize { property.value = @abc.humidistat.dehumidifier_mode = value.to_sym }
447
- end
448
- node.property("dehumidification-target",
449
- "Dehumidification Target Relative Humidity",
450
- :integer,
451
- @abc.humidistat.dehumidification_target,
452
- unit: "%",
453
- format: 35..65) do |value, property|
454
- @mutex.synchronize { property.value = @abc.humidistat.dehumidification_target = value }
491
+ if @abc.awl_communicating?
492
+ node.property("dehumidifier-mode",
493
+ "Dehumidifier Mode",
494
+ :enum,
495
+ @abc.humidistat.dehumidifier_mode,
496
+ format: %i[auto manual]) do |value, property|
497
+ @mutex.synchronize { property.value = @abc.humidistat.dehumidifier_mode = value.to_sym }
498
+ end
499
+ node.property("dehumidification-target",
500
+ "Dehumidification Target Relative Humidity",
501
+ :integer,
502
+ @abc.humidistat.dehumidification_target,
503
+ unit: "%",
504
+ format: 35..65) do |value, property|
505
+ @mutex.synchronize { property.value = @abc.humidistat.dehumidification_target = value }
506
+ end
507
+ node.hass_humidifier("dehumidifier-running",
508
+ target_property: "dehumidification-target",
509
+ mode_property: "dehumidifier-mode",
510
+ id: "dehumidifier",
511
+ name: "Dehumidifier",
512
+ device_class: :dehumidifier)
455
513
  end
456
- node.hass_humidifier("dehumidifier-running",
457
- target_property: "dehumidification-target",
458
- mode_property: "dehumidifier-mode",
459
- id: "dehumidifier",
460
- name: "Dehumidifier",
461
- device_class: :dehumidifier)
462
514
  end
515
+ next unless @abc.awl_communicating?
516
+
517
+ node.property("relative-humidity",
518
+ "Relative Humidity",
519
+ :integer,
520
+ @abc.humidistat.relative_humidity,
521
+ unit: "%",
522
+ format: 0..100,
523
+ hass: { sensor: { device_class: :humidity,
524
+ state_class: :measurement } })
463
525
  end
464
526
 
465
527
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
@@ -487,6 +549,14 @@ class MQTTBridge
487
549
  @abc.zones.each_with_index do |zone, i|
488
550
  type = zone.is_a?(Aurora::IZ2Zone) ? "IntelliZone 2 Zone" : "Thermostat"
489
551
  @homie.node("zone#{i + 1}", "Zone #{i + 1}", type) do |node|
552
+ node.property("current-mode",
553
+ "Current Heating/Cooling Mode Requested",
554
+ :enum,
555
+ zone.current_mode,
556
+ format: %w[standby h1 h2 h3 c1 c2])
557
+
558
+ next unless @abc.awl_communicating?
559
+
490
560
  allowed_modes = %w[off auto cool heat]
491
561
  allowed_modes << "eheat" if i.zero?
492
562
  node.property("target-mode",
@@ -496,11 +566,6 @@ class MQTTBridge
496
566
  format: allowed_modes) do |value, property|
497
567
  @mutex.synchronize { property.value = zone.target_mode = value.to_sym }
498
568
  end
499
- node.property("current-mode",
500
- "Current Heating/Cooling Mode Requested",
501
- :enum,
502
- zone.current_mode,
503
- format: %w[standby h1 h2 h3 c1 c2])
504
569
  node.property("target-fan-mode",
505
570
  "Target Fan Mode",
506
571
  :enum,
@@ -3,6 +3,7 @@
3
3
  require "yaml"
4
4
  require "uri"
5
5
 
6
+ require "aurora/aux_heat"
6
7
  require "aurora/blower"
7
8
  require "aurora/compressor"
8
9
  require "aurora/dhw"
@@ -40,12 +41,57 @@ module Aurora
40
41
  client = ::ModBus::RTUClient.new(io)
41
42
  client.with_slave(1)
42
43
  end
44
+
45
+ def query_registers(modbus_slave, query, try_individual: false)
46
+ implicit = try_individual
47
+ ranges = query.split(",").map do |addr|
48
+ case addr
49
+ when "known"
50
+ implicit = true
51
+ try_individual = true if try_individual.nil?
52
+ Aurora::REGISTER_NAMES.keys
53
+ when "valid"
54
+ implicit = true
55
+ try_individual = true if try_individual.nil?
56
+ break Aurora::REGISTER_RANGES
57
+ when /^(\d+)(?:\.\.|-)(\d+)$/
58
+ $1.to_i..$2.to_i
59
+ else
60
+ addr.to_i
61
+ end
62
+ end
63
+ queries = Aurora.normalize_ranges(ranges)
64
+ registers = {}
65
+ queries.each do |subquery|
66
+ registers.merge!(modbus_slave.read_multiple_holding_registers(*subquery))
67
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
68
+ # maybe this unit doesn't respond to all the addresses we want?
69
+ raise unless implicit
70
+
71
+ # try each query individually
72
+ subquery.each do |subsubquery|
73
+ registers.merge!(modbus_slave.read_multiple_holding_registers(subsubquery))
74
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
75
+ next unless try_individual
76
+
77
+ # seriously?? try each register individually
78
+ subsubquery.each do |i|
79
+ registers[i] = modbus_slave.holding_registers[i]
80
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
81
+ next
82
+ end
83
+ end
84
+ end
85
+ registers
86
+ end
43
87
  end
44
88
 
45
89
  attr_reader :modbus_slave,
90
+ :abc_version,
46
91
  :model,
47
92
  :serial_number,
48
93
  :zones,
94
+ :aux_heat,
49
95
  :compressor,
50
96
  :blower,
51
97
  :pump,
@@ -60,21 +106,22 @@ module Aurora
60
106
  :outdoor_temperature,
61
107
  :fp1,
62
108
  :fp2,
63
- :aux_heat_watts,
64
- :total_watts
109
+ :line_voltage,
110
+ :watts
65
111
 
66
112
  def initialize(uri)
67
113
  @modbus_slave = self.class.open_modbus_slave(uri)
68
114
  @modbus_slave.read_retry_timeout = 15
69
115
  @modbus_slave.read_retries = 2
70
- raw_registers = @modbus_slave.holding_registers[33, 88...110, 404, 412..413, 1103, 1114]
116
+ raw_registers = @modbus_slave.holding_registers[2, 33, 88...110, 404, 412..413, 813, 1103, 1114]
71
117
  registers = Aurora.transform_registers(raw_registers.dup)
118
+ @abc_version = registers[2]
72
119
  @program = registers[88]
73
120
  @model = registers[92]
74
121
  @serial_number = registers[105]
75
122
  @energy_monitor = raw_registers[412]
76
123
 
77
- @zones = if iz2?
124
+ @zones = if iz2? && iz2_version >= 2.0
78
125
  iz2_zone_count = @modbus_slave.holding_registers[483]
79
126
  (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
80
127
  else
@@ -83,6 +130,7 @@ module Aurora
83
130
 
84
131
  @abc_dipswitches = registers[33]
85
132
  @axb_dipswitches = registers[1103]
133
+ @aux_heat = AuxHeat.new(self)
86
134
  @compressor = if @program == "ABCVSP"
87
135
  Compressor::VSDrive.new(self)
88
136
  else
@@ -108,12 +156,13 @@ module Aurora
108
156
 
109
157
  @faults = []
110
158
 
111
- @registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1104, 1110..1111, 1114, 1150..1153, 1165,
112
- 31_003]
159
+ @registers_to_read = [6, 19..20, 25, 30, 112, 344, 567, 1104, 1110..1111, 1114, 1150..1153, 1165]
160
+ @registers_to_read.concat([741, 31_003]) if awl_communicating?
161
+ @registers_to_read << 900 if awl_axb?
113
162
  zones.each do |z|
114
163
  @registers_to_read.concat(z.registers_to_read)
115
164
  end
116
- @components = [compressor, blower, pump, dhw, humidistat].compact
165
+ @components = [aux_heat, compressor, blower, pump, dhw, humidistat].compact
117
166
  @components.each do |component|
118
167
  @registers_to_read.concat(component.registers_to_read)
119
168
  end
@@ -122,37 +171,7 @@ module Aurora
122
171
  end
123
172
 
124
173
  def query_registers(query)
125
- implicit = false
126
- ranges = query.split(",").map do |addr|
127
- case addr
128
- when "known"
129
- implicit = true
130
- Aurora::REGISTER_NAMES.keys
131
- when "valid"
132
- implicit = true
133
- break Aurora::REGISTER_RANGES
134
- when /^(\d+)(?:\.\.|-)(\d+)$/
135
- $1.to_i..$2.to_i
136
- else
137
- addr.to_i
138
- end
139
- end
140
- queries = Aurora.normalize_ranges(ranges)
141
- registers = {}
142
- queries.each do |subquery|
143
- registers.merge!(@modbus_slave.read_multiple_holding_registers(*subquery))
144
- rescue ::ModBus::Errors::IllegalDataAddress
145
- # maybe this unit doesn't respond to all the addresses we want?
146
- raise unless implicit
147
-
148
- # try each query individually
149
- subquery.each do |subsubquery|
150
- registers.merge!(@modbus_slave.read_multiple_holding_registers(subsubquery))
151
- rescue ::ModBus::Errors::IllegalDataAddress
152
- next
153
- end
154
- end
155
- registers
174
+ self.class.query_registers(@modbus_slave, query)
156
175
  end
157
176
 
158
177
  def refresh
@@ -164,8 +183,8 @@ module Aurora
164
183
 
165
184
  outputs = registers[30]
166
185
 
167
- @entering_air_temperature = registers[740]
168
- @leaving_air_temperature = registers[900]
186
+ @entering_air_temperature = registers[567]
187
+ @leaving_air_temperature = registers[900] if awl_axb?
169
188
  @leaving_water_temperature = registers[1110]
170
189
  @entering_water_temperature = registers[1111]
171
190
  @outdoor_temperature = registers[31_003]
@@ -175,8 +194,8 @@ module Aurora
175
194
  @error = registers[25] & 0x7fff
176
195
  @derated = (41..46).cover?(@error)
177
196
  @safe_mode = [47, 48, 49, 72, 74].include?(@error)
178
- @aux_heat_watts = registers[1151]
179
- @total_watts = registers[1153]
197
+ @line_voltage = registers[112]
198
+ @watts = registers[1153]
180
199
 
181
200
  @current_mode = if outputs.include?(:lockout)
182
201
  :lockout
@@ -184,10 +203,6 @@ module Aurora
184
203
  :dehumidify
185
204
  elsif outputs.include?(:cc2) || outputs.include?(:cc)
186
205
  outputs.include?(:rv) ? :cooling : :heating
187
- elsif outputs.include?(:eh2)
188
- outputs.include?(:rv) ? :eh2 : :emergency
189
- elsif outputs.include?(:eh1)
190
- outputs.include?(:rv) ? :eh1 : :emergency
191
206
  elsif outputs.include?(:blower)
192
207
  :blower
193
208
  elsif registers[6]
@@ -255,13 +270,18 @@ module Aurora
255
270
  end
256
271
 
257
272
  # config aurora system
258
- { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
273
+ { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824, awl: 827 }.each do |(component, register)|
259
274
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
260
275
  def #{component}?
261
276
  return @#{component} if instance_variable_defined?(:@#{component})
262
277
  @#{component} = @modbus_slave.holding_registers[#{register}] != 3
263
278
  end
264
279
 
280
+ def #{component}_version
281
+ return @#{component}_version if instance_variable_defined?(:@#{component}_version)
282
+ @#{component}_version = @modbus_slave.holding_registers[#{register + 1}].to_f / 100
283
+ end
284
+
265
285
  def add_#{component}
266
286
  @modbus_slave.holding_registers[#{register}] = 2
267
287
  end
@@ -272,6 +292,27 @@ module Aurora
272
292
  RUBY
273
293
  end
274
294
 
295
+ # see https://www.waterfurnace.com/literature/symphony/ig2001ew.pdf
296
+ # is there a communicating system compliant with AWL?
297
+ def awl_communicating?
298
+ awl_thermostat? || awl_iz2?
299
+ end
300
+
301
+ # is the thermostat AWL compliant?
302
+ def awl_thermostat?
303
+ thermostat? && thermostat_version >= 3.0
304
+ end
305
+
306
+ # is the IZ2 AWL compliant?
307
+ def awl_iz2?
308
+ iz2? && iz2_version >= 2.0
309
+ end
310
+
311
+ # is the AXB AWL compliant?
312
+ def awl_axb?
313
+ axb? && axb_version >= 2.0
314
+ end
315
+
275
316
  def inspect
276
317
  "#<Aurora::ABCClient #{(instance_variables - [:@modbus_slave]).map do |iv|
277
318
  "#{iv}=#{instance_variable_get(iv).inspect}"
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aurora/component"
4
+
5
+ module Aurora
6
+ class AuxHeat < Component
7
+ attr_reader :stage, :watts
8
+
9
+ def refresh(registers)
10
+ outputs = registers[30]
11
+ @stage = if outputs.include?(:eh2)
12
+ 2
13
+ elsif outputs.include?(:eh1)
14
+ 1
15
+ else
16
+ 0
17
+ end
18
+ @watts = registers[1151] if abc.energy_monitoring?
19
+ end
20
+ end
21
+ end
data/lib/aurora/blower.rb CHANGED
@@ -36,7 +36,7 @@ module Aurora
36
36
 
37
37
  def refresh(registers)
38
38
  outputs = registers[30]
39
- @speed = if outputs.include?(:eh1)
39
+ @speed = if outputs.include?(:eh1) || outputs.include?(:eh2)
40
40
  4
41
41
  elsif outputs.include?(:cc2)
42
42
  3
@@ -6,6 +6,10 @@ module Aurora
6
6
  @abc = abc
7
7
  end
8
8
 
9
+ def registers_to_read
10
+ []
11
+ end
12
+
9
13
  def inspect
10
14
  "#<Aurora::#{self.class.name} #{(instance_variables - [:@abc]).map do |iv|
11
15
  "#{iv}=#{instance_variable_get(iv).inspect}"
@@ -42,7 +42,7 @@ module Aurora
42
42
  end
43
43
 
44
44
  class VSDrive < GenericCompressor
45
- attr_reader :ambient_temperature, :iz2_desired_speed
45
+ attr_reader :drive_temperature, :inverter_temperature, :ambient_temperature, :iz2_desired_speed, :fan_speed
46
46
 
47
47
  def initialize(abc)
48
48
  super(abc, 12)
@@ -53,7 +53,7 @@ module Aurora
53
53
  end
54
54
 
55
55
  def registers_to_read
56
- result = super + [209, 3001, 3326]
56
+ result = super + [209, 3001, 3326..3327, 3522, 3524]
57
57
  result << 564 if abc.iz2?
58
58
  result
59
59
  end
@@ -63,6 +63,10 @@ module Aurora
63
63
 
64
64
  @speed = registers[3001]
65
65
  @ambient_temperature = registers[3326]
66
+ @drive_temperature = registers[3327]
67
+ @inverter_temperature = registers[3522]
68
+ @fan_speed = registers[3524]
69
+
66
70
  @iz2_desired_speed = registers[564] if abc.iz2?
67
71
  end
68
72
  end
@@ -29,6 +29,8 @@ module Aurora
29
29
  alias_method :dehumidifier_running?, :dehumidifier_running
30
30
 
31
31
  def registers_to_read
32
+ return [] unless @abc.awl_communicating?
33
+
32
34
  result = [741]
33
35
  if humidifier? || dehumidifier? || abc.compressor.is_a?(Compressor::VSDrive)
34
36
  result.concat(abc.iz2? ? [21_114, 31_109..31_110] : [12_309..12_310])
@@ -20,9 +20,14 @@ module Aurora
20
20
  if register.length == 1
21
21
  case register.first
22
22
  when Integer
23
+ missing_register(register.first) unless @registers.key?(register.first)
23
24
  @registers[register.first]
24
25
  when Range
25
- @registers.values_at(*register.first.to_a)
26
+ registers = register.first.to_a
27
+ registers.each do |i|
28
+ missing_register(i) unless @registers.key?(i)
29
+ end
30
+ @registers.values_at(*registers)
26
31
  else
27
32
  raise ArgumentError, "Not implemented yet #{register.inspect}"
28
33
  end
@@ -35,6 +40,11 @@ module Aurora
35
40
  result = {}
36
41
  queries.each do |query|
37
42
  Array(query).each do |i|
43
+ unless @registers.key?(i)
44
+ missing_register(i)
45
+ next
46
+ end
47
+
38
48
  result[i] = @registers[i]
39
49
  end
40
50
  end
@@ -45,5 +55,11 @@ module Aurora
45
55
  @registers[addr] = value
46
56
  end
47
57
  alias_method :[]=, :write_holding_register
58
+
59
+ private
60
+
61
+ def missing_register(idx)
62
+ logger.warn("missing register #{idx}")
63
+ end
48
64
  end
49
65
  end
data/lib/aurora/pump.rb CHANGED
@@ -31,7 +31,7 @@ module Aurora
31
31
  alias_method :manual_control?, :manual_control
32
32
 
33
33
  def registers_to_read
34
- super + [321..325]
34
+ super + (@abc.awl_axb? ? [321..325] : [321..324])
35
35
  end
36
36
 
37
37
  def refresh(registers)
@@ -39,7 +39,7 @@ module Aurora
39
39
  @minimum_speed = registers[321]
40
40
  @maximum_speed = registers[322]
41
41
  @manual_control = registers[323] != :off
42
- @speed = registers[325]
42
+ @speed = registers[325] if @abc.awl_axb?
43
43
  end
44
44
 
45
45
  def manual_control=(value)
@@ -65,7 +65,7 @@ module Aurora
65
65
 
66
66
  def to_string(registers, idx, length)
67
67
  (idx...(idx + length)).map do |i|
68
- raise ArgumentError, "Missing register #{i} for string starting at #{idx}" unless registers[i]
68
+ next "\ufffd" unless registers[i] # missing data? add unicode invalid character
69
69
 
70
70
  (registers[i] >> 8).chr + (registers[i] & 0xff).chr
71
71
  end.join.sub(/[ \0]+$/, "")
@@ -112,7 +112,7 @@ module Aurora
112
112
  53 => "Condensing Pressure Sensor", # Low condensing pressure (PD) or invalid (0 to 870 psi) Retry 10x.
113
113
  54 => "Low Supply Voltage", # Supply Voltage is <180 V (190V to reset) or powered off/on too quickly (<30 sec.).
114
114
  55 => "Out of Envelope", # Comp Operating out of envelope (P0) more than 90 sec. Retry 10x.
115
- 56 => "Drive Over Currnet", # Over current tripped by phase loss, earth fault, short circuit, low water flow, low air flow, or major drive fault. # rubocop:disable Layout/LineLength
115
+ 56 => "Drive Over Current", # Over current tripped by phase loss, earth fault, short circuit, low water flow, low air flow, or major drive fault. # rubocop:disable Layout/LineLength
116
116
  57 => "Drive Over/Under Voltage", # DC Link Voltage to compressor is >450vdc or at minimum voltage (<185vdc).
117
117
  58 => "High Drive Temp", # Drive Temp has reached critical High Temp >239 F
118
118
  59 => "Internal Drive Error", # The MOC has encountered an internal fault or an internal error. Probably fatal.
@@ -867,11 +867,11 @@ module Aurora
867
867
  3225 => "VS Drive Details (Safemode 2)",
868
868
  3226 => "VS Drive Details (Alarm 1)",
869
869
  3227 => "VS Drive Details (Alarm 2)",
870
- 3327 => "VS Drive Temperature",
871
870
  3322 => "VS Drive Discharge Pressure",
872
871
  3323 => "VS Drive Suction Pressure",
873
872
  3325 => "VS Drive Discharge Temperature",
874
873
  3326 => "VS Drive Compressor Ambient Temperature",
874
+ 3327 => "VS Drive Temperature",
875
875
  3330 => "VS Drive Entering Water Temperature",
876
876
  3331 => "VS Drive Line Voltage",
877
877
  3332 => "VS Drive Thermo Power",
@@ -15,19 +15,23 @@ module Aurora
15
15
  :fan_intermittent_off
16
16
 
17
17
  def registers_to_read
18
+ return [31] unless @abc.awl_thermostat?
19
+
18
20
  [31, 502, 745..746, 12_005..12_006]
19
21
  end
20
22
 
21
23
  def refresh(registers)
22
- @ambient_temperature = registers[502]
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]
24
+ if @abc.awl_thermostat?
25
+ @ambient_temperature = registers[502]
26
+ @heating_target_temperature = registers[745]
27
+ @cooling_target_temperature = registers[746]
28
+ config1 = registers[12_005]
29
+ config2 = registers[12_006]
30
+ @target_fan_mode = config1[:fan]
31
+ @fan_intermittent_on = config1[:on_time]
32
+ @fan_intermittent_off = config1[:off_time]
33
+ @target_mode = config2[:mode]
34
+ end
31
35
 
32
36
  inputs = registers[31]
33
37
  @current_fan_mode = inputs.include?(:g)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aurora
4
- VERSION = "0.7.5"
4
+ VERSION = "1.0.0"
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.7.5
4
+ version: 1.0.0
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-12-11 00:00:00.000000000 Z
11
+ date: 2022-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ccutrer-serialport
@@ -166,6 +166,7 @@ files:
166
166
  - exe/web_aid_tool
167
167
  - lib/aurora.rb
168
168
  - lib/aurora/abc_client.rb
169
+ - lib/aurora/aux_heat.rb
169
170
  - lib/aurora/blower.rb
170
171
  - lib/aurora/component.rb
171
172
  - lib/aurora/compressor.rb