waterfurnace_aurora 0.7.6 → 1.0.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: 6682276ad275ed03cc9985a993fb2b97830d4aee801d544797b7dbe4e34288db
4
- data.tar.gz: 674d421a8729c071bab6de126757d993eb4940a0ee6e514b8e689397ceee1c56
3
+ metadata.gz: 8311d85ee1e300bcfbb3228a65cb98a69301f25f27569c36d898faf97c25fbd5
4
+ data.tar.gz: 86eab096d17d12aa01bb56f253022d64bb806d5aec3b55c6efc16a895b75d111
5
5
  SHA512:
6
- metadata.gz: a47a7971f42f9ad100cc756b06b6146fc2c315a4298af501478f7208697d31bbd535ee26e7cd1250f4391bb7df16cd07da5ea420e752cfb95d6c4e1192ae9021
7
- data.tar.gz: 2fdd41be490e09ae5436508b972134ee3f51c2b07b24f21006fccec51fd9981a79715a380ef401ad76a5c5680cf329b023b8f2e2f3a53cf3b8f2a78c08a71c73
6
+ metadata.gz: 7edd9fd909c0b9fe316f306bf00a56eff1108f467718d71c1645596675938abd67c5e727b4f6312d79c1d706df0f22f7d65f475c35061f7b66405add92eec342
7
+ data.tar.gz: b0a6f4af036ebcd003557777e1fd276e33e56e1cbfbde268ad42a5a5bfa729c9ffa718dd3fd08917aad8beba2bf4d894519de128a5ba1519b5988ba5ddc707f7
@@ -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 heating_with_aux emergency_heat 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,
@@ -186,8 +190,8 @@ class MQTTBridge
186
190
  :integer,
187
191
  @abc.line_voltage,
188
192
  format: 90..635,
189
- unit: "V") do |value, property|
190
- @mutex.synchronize { property.value = @abc.line_voltage = value }
193
+ unit: "V") do |value|
194
+ @mutex.synchronize { @abc.line_voltage = value }
191
195
  end
192
196
  node.property("fp1",
193
197
  "FP1 Sensor",
@@ -205,12 +209,11 @@ class MQTTBridge
205
209
  hass: { sensor: { device_class: :temperature,
206
210
  state_class: :measurement,
207
211
  entity_category: :diagnostic } })
208
- %i[aux_heat total].each do |component|
209
- component = "#{component}_watts"
210
- node.property(component.tr("_", "-"),
211
- component.sub("_watts", " Power Usage").tr("_", " ").titleize,
212
+ if @abc.energy_monitoring?
213
+ node.property("watts",
214
+ "Total Power Usage",
212
215
  :integer,
213
- @abc.public_send(component),
216
+ @abc.watts,
214
217
  unit: "W",
215
218
  hass: { sensor: { device_class: :power,
216
219
  state_class: :measurement } })
@@ -244,6 +247,30 @@ class MQTTBridge
244
247
  hass: { sensor: { device_class: :temperature,
245
248
  state_class: :measurement,
246
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 } })
247
274
 
248
275
  next unless @abc.iz2?
249
276
 
@@ -256,6 +283,25 @@ class MQTTBridge
256
283
  entity_category: :diagnostic } })
257
284
  end
258
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
+
259
305
  @blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
260
306
  node.property("running",
261
307
  "Running",
@@ -291,8 +337,8 @@ class MQTTBridge
291
337
  :integer,
292
338
  @abc.blower.public_send(field),
293
339
  format: 1..12,
294
- hass: { number: { entity_category: :config } }) do |value, property|
295
- @mutex.synchronize { property.value = @abc.blower.public_send("#{field}=", value) }
340
+ hass: { number: { entity_category: :config } }) do |value|
341
+ @mutex.synchronize { @abc.blower.public_send("#{field}=", value) }
296
342
  end
297
343
  end
298
344
 
@@ -333,21 +379,12 @@ class MQTTBridge
333
379
  :boolean,
334
380
  @abc.pump.running,
335
381
  hass: { binary_sensor: { device_class: :running } })
336
- node.property("speed",
337
- "Speed",
338
- :integer,
339
- @abc.pump.speed,
340
- format: 0..100,
341
- unit: "%",
342
- hass: { number: { entity_category: :diagnostic } }) do |value, property|
343
- @mutex.synchronize { property.value = @abc.pump.speed = value }
344
- end
345
382
  node.property("manual-control",
346
383
  "Manual Control",
347
384
  :boolean,
348
385
  @abc.pump.manual_control?,
349
- hass: { switch: { entity_category: :diagnostic } }) do |value, property|
350
- @mutex.synchronize { property.value = @abc.pump.manual_control = value }
386
+ hass: { switch: { entity_category: :diagnostic } }) do |value|
387
+ @mutex.synchronize { @abc.pump.manual_control = value }
351
388
  end
352
389
  node.property("minimum-speed",
353
390
  "Actual Minimum Speed",
@@ -355,8 +392,8 @@ class MQTTBridge
355
392
  @abc.pump.minimum_speed,
356
393
  format: 0..100,
357
394
  unit: "%",
358
- hass: { number: { entity_category: :config } }) do |value, property|
359
- @mutex.synchronize { property.value = @abc.pump.minimum_speed = value }
395
+ hass: { number: { entity_category: :config } }) do |value|
396
+ @mutex.synchronize { @abc.pump.minimum_speed = value }
360
397
  end
361
398
  node.property("maximum-speed",
362
399
  "Actual Maximum Speed",
@@ -364,8 +401,19 @@ class MQTTBridge
364
401
  @abc.pump.minimum_speed,
365
402
  format: 0..100,
366
403
  unit: "%",
367
- hass: { number: { entity_category: :config } }) do |value, property|
368
- @mutex.synchronize { property.value = @abc.pump.maximum_speed = value }
404
+ hass: { number: { entity_category: :config } }) do |value|
405
+ @mutex.synchronize { @abc.pump.maximum_speed = value }
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|
416
+ @mutex.synchronize { @abc.pump.speed = value }
369
417
  end
370
418
  end
371
419
 
@@ -375,8 +423,8 @@ class MQTTBridge
375
423
  "Enabled",
376
424
  :boolean,
377
425
  @abc.dhw.enabled,
378
- hass: { switch: { icon: "mdi:water-boiler" } }) do |value, property|
379
- @mutex.synchronize { property.value = @abc.dhw.enabled = value }
426
+ hass: { switch: { icon: "mdi:water-boiler" } }) do |value|
427
+ @mutex.synchronize { @abc.dhw.enabled = value }
380
428
  end
381
429
  node.property("running",
382
430
  "Pump Running",
@@ -396,47 +444,41 @@ class MQTTBridge
396
444
  @abc.dhw.set_point,
397
445
  format: 100..140,
398
446
  unit: "°F",
399
- hass: :number) do |value, property|
400
- @mutex.synchronize { property.value = @abc.dhw.set_point = value }
447
+ hass: { number: { step: 5 } }) do |value|
448
+ @mutex.synchronize { @abc.dhw.set_point = value }
401
449
  end
402
450
  end
403
451
  end
404
452
 
405
453
  @humidistat = @homie.node("humidistat", "Humidistat", "Humidistat") do |node|
406
- node.property("relative-humidity",
407
- "Relative Humidity",
408
- :integer,
409
- @abc.humidistat.relative_humidity,
410
- unit: "%",
411
- format: 0..100,
412
- hass: { sensor: { device_class: :humidity,
413
- state_class: :measurement } })
414
454
  if @abc.humidistat.humidifier?
415
455
  node.property("humidifier-running",
416
456
  "Humidifier Running",
417
457
  :boolean,
418
458
  @abc.humidistat.humidifier_running?)
419
- node.property("humidifier-mode",
420
- "Humidifier Mode",
421
- :enum,
422
- @abc.humidistat.humidifier_mode,
423
- format: %i[auto manual]) do |value, property|
424
- @mutex.synchronize { property.value = @abc.humidistat.humidifier_mode = value.to_sym }
425
- end
426
- node.property("humidification-target",
427
- "Humidification Target Relative Humidity",
428
- :integer,
429
- @abc.humidistat.humidification_target,
430
- unit: "%",
431
- format: 15..50) do |value, property|
432
- @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|
465
+ @mutex.synchronize { @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|
473
+ @mutex.synchronize { @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)
433
481
  end
434
- node.hass_humidifier("humidifier-running",
435
- target_property: "humidification-target",
436
- mode_property: "humidifier-mode",
437
- id: "humidifier",
438
- name: "Humidifier",
439
- device_class: :humidifier)
440
482
  end
441
483
 
442
484
  # VSDrive can perform active dehumidification, even without a dedicated dehumidifier
@@ -446,28 +488,40 @@ class MQTTBridge
446
488
  "Dehumidifier Running",
447
489
  :boolean,
448
490
  @abc.humidistat.dehumidifier_running?)
449
- node.property("dehumidifier-mode",
450
- "Dehumidifier Mode",
451
- :enum,
452
- @abc.humidistat.dehumidifier_mode,
453
- format: %i[auto manual]) do |value, property|
454
- @mutex.synchronize { property.value = @abc.humidistat.dehumidifier_mode = value.to_sym }
455
- end
456
- node.property("dehumidification-target",
457
- "Dehumidification Target Relative Humidity",
458
- :integer,
459
- @abc.humidistat.dehumidification_target,
460
- unit: "%",
461
- format: 35..65) do |value, property|
462
- @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|
497
+ @mutex.synchronize { @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|
505
+ @mutex.synchronize { @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)
463
513
  end
464
- node.hass_humidifier("dehumidifier-running",
465
- target_property: "dehumidification-target",
466
- mode_property: "dehumidifier-mode",
467
- id: "dehumidifier",
468
- name: "Dehumidifier",
469
- device_class: :dehumidifier)
470
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 } })
471
525
  end
472
526
 
473
527
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
@@ -495,26 +549,29 @@ class MQTTBridge
495
549
  @abc.zones.each_with_index do |zone, i|
496
550
  type = zone.is_a?(Aurora::IZ2Zone) ? "IntelliZone 2 Zone" : "Thermostat"
497
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
+
498
560
  allowed_modes = %w[off auto cool heat]
499
561
  allowed_modes << "eheat" if i.zero?
500
562
  node.property("target-mode",
501
563
  "Target Heating/Cooling Mode",
502
564
  :enum,
503
565
  zone.target_mode,
504
- format: allowed_modes) do |value, property|
505
- @mutex.synchronize { property.value = zone.target_mode = value.to_sym }
566
+ format: allowed_modes) do |value|
567
+ @mutex.synchronize { zone.target_mode = value.to_sym }
506
568
  end
507
- node.property("current-mode",
508
- "Current Heating/Cooling Mode Requested",
509
- :enum,
510
- zone.current_mode,
511
- format: %w[standby h1 h2 h3 c1 c2])
512
569
  node.property("target-fan-mode",
513
570
  "Target Fan Mode",
514
571
  :enum,
515
572
  zone.target_fan_mode,
516
- format: %w[auto continuous intermittent]) do |value, property|
517
- @mutex.synchronize { property.value = zone.target_fan_mode = value.to_sym }
573
+ format: %w[auto continuous intermittent]) do |value|
574
+ @mutex.synchronize { zone.target_fan_mode = value.to_sym }
518
575
  end
519
576
  node.property("fan-intermittent-on",
520
577
  "Fan Intermittent Mode On Duration",
@@ -522,8 +579,8 @@ class MQTTBridge
522
579
  zone.fan_intermittent_on,
523
580
  unit: "M",
524
581
  format: %w[0 5 10 15 20],
525
- hass: { select: { entity_category: :config } }) do |value, property|
526
- @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
582
+ hass: { select: { entity_category: :config } }) do |value|
583
+ @mutex.synchronize { zone.fan_intermittent_on = value.to_i }
527
584
  end
528
585
  node.property("fan-intermittent-off",
529
586
  "Fan Intermittent Mode Off Duration",
@@ -531,8 +588,8 @@ class MQTTBridge
531
588
  zone.fan_intermittent_on,
532
589
  unit: "M",
533
590
  format: %w[0 5 10 15 20 25 30 35 40],
534
- hass: { select: { entity_category: :config } }) do |value, property|
535
- @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
591
+ hass: { select: { entity_category: :config } }) do |value|
592
+ @mutex.synchronize { zone.fan_intermittent_on = value.to_i }
536
593
  end
537
594
  node.property("current-fan-mode",
538
595
  "Current Fan Status",
@@ -573,15 +630,15 @@ class MQTTBridge
573
630
  :integer,
574
631
  zone.heating_target_temperature,
575
632
  unit: "°F",
576
- format: 40..90) do |value, property|
577
- @mutex.synchronize { property.value = zone.heating_target_temperature = value }
633
+ format: 40..90) do |value|
634
+ @mutex.synchronize { zone.heating_target_temperature = value }
578
635
  end
579
636
  node.property("cooling-target-temperature",
580
637
  "Cooling Target Temperature", :integer,
581
638
  zone.cooling_target_temperature,
582
639
  unit: "°F",
583
- format: 54..99) do |value, property|
584
- @mutex.synchronize { property.value = zone.cooling_target_temperature = value }
640
+ format: 54..99) do |value|
641
+ @mutex.synchronize { zone.cooling_target_temperature = value }
585
642
  end
586
643
  node.hass_climate(action_property: "current-mode",
587
644
  current_temperature_property: "ambient-temperature",
@@ -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"
@@ -63,20 +64,20 @@ module Aurora
63
64
  registers = {}
64
65
  queries.each do |subquery|
65
66
  registers.merge!(modbus_slave.read_multiple_holding_registers(*subquery))
66
- rescue ::ModBus::Errors::IllegalDataAddress
67
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
67
68
  # maybe this unit doesn't respond to all the addresses we want?
68
69
  raise unless implicit
69
70
 
70
71
  # try each query individually
71
72
  subquery.each do |subsubquery|
72
73
  registers.merge!(modbus_slave.read_multiple_holding_registers(subsubquery))
73
- rescue ::ModBus::Errors::IllegalDataAddress
74
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
74
75
  next unless try_individual
75
76
 
76
77
  # seriously?? try each register individually
77
78
  subsubquery.each do |i|
78
79
  registers[i] = modbus_slave.holding_registers[i]
79
- rescue ::ModBus::Errors::IllegalDataAddress
80
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
80
81
  next
81
82
  end
82
83
  end
@@ -86,9 +87,11 @@ module Aurora
86
87
  end
87
88
 
88
89
  attr_reader :modbus_slave,
90
+ :abc_version,
89
91
  :model,
90
92
  :serial_number,
91
93
  :zones,
94
+ :aux_heat,
92
95
  :compressor,
93
96
  :blower,
94
97
  :pump,
@@ -104,21 +107,21 @@ module Aurora
104
107
  :fp1,
105
108
  :fp2,
106
109
  :line_voltage,
107
- :aux_heat_watts,
108
- :total_watts
110
+ :watts
109
111
 
110
112
  def initialize(uri)
111
113
  @modbus_slave = self.class.open_modbus_slave(uri)
112
114
  @modbus_slave.read_retry_timeout = 15
113
115
  @modbus_slave.read_retries = 2
114
- 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]
115
117
  registers = Aurora.transform_registers(raw_registers.dup)
118
+ @abc_version = registers[2]
116
119
  @program = registers[88]
117
120
  @model = registers[92]
118
121
  @serial_number = registers[105]
119
122
  @energy_monitor = raw_registers[412]
120
123
 
121
- @zones = if iz2?
124
+ @zones = if iz2? && iz2_version >= 2.0
122
125
  iz2_zone_count = @modbus_slave.holding_registers[483]
123
126
  (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
124
127
  else
@@ -127,6 +130,7 @@ module Aurora
127
130
 
128
131
  @abc_dipswitches = registers[33]
129
132
  @axb_dipswitches = registers[1103]
133
+ @aux_heat = AuxHeat.new(self)
130
134
  @compressor = if @program == "ABCVSP"
131
135
  Compressor::VSDrive.new(self)
132
136
  else
@@ -152,12 +156,13 @@ module Aurora
152
156
 
153
157
  @faults = []
154
158
 
155
- @registers_to_read = [6, 19..20, 25, 30, 112, 344, 740..741, 900, 1104, 1110..1111, 1114, 1150..1153, 1165,
156
- 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?
157
162
  zones.each do |z|
158
163
  @registers_to_read.concat(z.registers_to_read)
159
164
  end
160
- @components = [compressor, blower, pump, dhw, humidistat].compact
165
+ @components = [aux_heat, compressor, blower, pump, dhw, humidistat].compact
161
166
  @components.each do |component|
162
167
  @registers_to_read.concat(component.registers_to_read)
163
168
  end
@@ -178,8 +183,8 @@ module Aurora
178
183
 
179
184
  outputs = registers[30]
180
185
 
181
- @entering_air_temperature = registers[740]
182
- @leaving_air_temperature = registers[900]
186
+ @entering_air_temperature = registers[567]
187
+ @leaving_air_temperature = registers[900] if awl_axb?
183
188
  @leaving_water_temperature = registers[1110]
184
189
  @entering_water_temperature = registers[1111]
185
190
  @outdoor_temperature = registers[31_003]
@@ -190,22 +195,25 @@ module Aurora
190
195
  @derated = (41..46).cover?(@error)
191
196
  @safe_mode = [47, 48, 49, 72, 74].include?(@error)
192
197
  @line_voltage = registers[112]
193
- @aux_heat_watts = registers[1151]
194
- @total_watts = registers[1153]
198
+ @watts = registers[1153]
195
199
 
196
200
  @current_mode = if outputs.include?(:lockout)
197
201
  :lockout
198
202
  elsif registers[362]
199
203
  :dehumidify
200
204
  elsif outputs.include?(:cc2) || outputs.include?(:cc)
201
- outputs.include?(:rv) ? :cooling : :heating
202
- elsif outputs.include?(:eh2)
203
- outputs.include?(:rv) ? :eh2 : :emergency
204
- elsif outputs.include?(:eh1)
205
- outputs.include?(:rv) ? :eh1 : :emergency
205
+ if outputs.include?(:rv)
206
+ :cooling
207
+ elsif outputs.include?(:eh2) || outputs.include?(:eh1)
208
+ :heating_with_aux
209
+ else
210
+ :heating
211
+ end
212
+ elsif outputs.include?(:eh2) || outputs.include?(:eh1)
213
+ :emergency_heat
206
214
  elsif outputs.include?(:blower)
207
215
  :blower
208
- elsif registers[6]
216
+ elsif !registers[6].zero?
209
217
  :waiting
210
218
  else
211
219
  :standby
@@ -270,13 +278,18 @@ module Aurora
270
278
  end
271
279
 
272
280
  # config aurora system
273
- { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
281
+ { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824, awl: 827 }.each do |(component, register)|
274
282
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
275
283
  def #{component}?
276
284
  return @#{component} if instance_variable_defined?(:@#{component})
277
285
  @#{component} = @modbus_slave.holding_registers[#{register}] != 3
278
286
  end
279
287
 
288
+ def #{component}_version
289
+ return @#{component}_version if instance_variable_defined?(:@#{component}_version)
290
+ @#{component}_version = @modbus_slave.holding_registers[#{register + 1}].to_f / 100
291
+ end
292
+
280
293
  def add_#{component}
281
294
  @modbus_slave.holding_registers[#{register}] = 2
282
295
  end
@@ -287,6 +300,27 @@ module Aurora
287
300
  RUBY
288
301
  end
289
302
 
303
+ # see https://www.waterfurnace.com/literature/symphony/ig2001ew.pdf
304
+ # is there a communicating system compliant with AWL?
305
+ def awl_communicating?
306
+ awl_thermostat? || awl_iz2?
307
+ end
308
+
309
+ # is the thermostat AWL compliant?
310
+ def awl_thermostat?
311
+ thermostat? && thermostat_version >= 3.0
312
+ end
313
+
314
+ # is the IZ2 AWL compliant?
315
+ def awl_iz2?
316
+ iz2? && iz2_version >= 2.0
317
+ end
318
+
319
+ # is the AXB AWL compliant?
320
+ def awl_axb?
321
+ axb? && axb_version >= 2.0
322
+ end
323
+
290
324
  def inspect
291
325
  "#<Aurora::ABCClient #{(instance_variables - [:@modbus_slave]).map do |iv|
292
326
  "#{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
data/lib/aurora/dhw.rb CHANGED
@@ -27,7 +27,6 @@ module Aurora
27
27
 
28
28
  raw_value = (value * 10).to_i
29
29
  holding_registers[401] = raw_value
30
- @set_point = value
31
30
  end
32
31
  end
33
32
  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])
@@ -73,8 +75,6 @@ module Aurora
73
75
  raw_value |= 0x4000 if humidifier_mode == :auto
74
76
  raw_value |= 0x8000 if dehumidifier_mode == :auto
75
77
  holding_registers[abc.iz2? ? 21_114 : 12_309] = raw_value
76
- @humidifier_mode = humidifier_mode
77
- @dehumidifier_mode = dehumidifier_mode
78
78
  end
79
79
 
80
80
  def humidification_target=(value)
@@ -90,8 +90,6 @@ module Aurora
90
90
  raise ArgumentError unless (35..65).cover?(dehumidification_target)
91
91
 
92
92
  holding_registers[abc.iz2? ? 21_115 : 12_310] = (humidification_target << 8) + dehumidification_target
93
- @humidification_target = humidification_target
94
- @dehumidification_target = dehumidification_target
95
93
  end
96
94
  end
97
95
  end
@@ -46,28 +46,24 @@ module Aurora
46
46
  return unless (raw_value = Aurora::HEATING_MODE.invert[value])
47
47
 
48
48
  holding_registers[21_202 + ((zone_number - 1) * 9)] = raw_value
49
- @target_mode = value
50
49
  end
51
50
 
52
51
  def target_fan_mode=(value)
53
52
  return unless (raw_value = Aurora::FAN_MODE.invert[value])
54
53
 
55
54
  holding_registers[21_205 + ((zone_number - 1) * 9)] = raw_value
56
- @target_fan_mode = value
57
55
  end
58
56
 
59
57
  def fan_intermittent_on=(value)
60
58
  return unless value >= 0 && value <= 25 && (value % 5).zero?
61
59
 
62
60
  holding_registers[21_206 + ((zone_number - 1) * 9)] = value
63
- @fan_intermittent_on = value
64
61
  end
65
62
 
66
63
  def fan_intermittent_off=(value)
67
64
  return unless value >= 0 && value <= 40 && (value % 5).zero?
68
65
 
69
66
  holding_registers[21_207 + ((zone_number - 1) * 9)] = value
70
- @fan_intermittent_off = value
71
67
  end
72
68
 
73
69
  def heating_target_temperature=(value)
@@ -75,7 +71,6 @@ module Aurora
75
71
 
76
72
  raw_value = (value * 10).to_i
77
73
  holding_registers[21_203 + ((zone_number - 1) * 9)] = raw_value
78
- @heating_target_temperature = value
79
74
  end
80
75
 
81
76
  def cooling_target_temperature=(value)
@@ -83,7 +78,6 @@ module Aurora
83
78
 
84
79
  raw_value = (value * 10).to_i
85
80
  holding_registers[21_204 + ((zone_number - 1) * 9)] = raw_value
86
- @cooling_target_temperature = value
87
81
  end
88
82
  end
89
83
  end
@@ -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.
@@ -229,6 +229,8 @@ module Aurora
229
229
  0x08 => :blower,
230
230
  0x10 => :eh1,
231
231
  0x20 => :eh2,
232
+ # 0x40 => ??, # this turns on and off quite a bit during normal operation
233
+ # 0x80 => ??, # this turns on occasionally during normal operation; I've only seen it when aux heat is on
232
234
  0x200 => :accessory,
233
235
  0x400 => :lockout,
234
236
  0x800 => :alarm
@@ -703,8 +705,9 @@ module Aurora
703
705
  9 => "Compressor Minimum Run Time",
704
706
  15 => "Blower Off Delay",
705
707
  16 => "Line Voltage",
706
- 17 => "Aux/E Heat Stage", # this has some complicated condition based on
707
- # current inputs and outputs on if it should have a value (310 - v) or (130 - v), or be 0
708
+ 17 => "Aux/E Heat Stage", # this is how long aux/eheat have been requested in seconds
709
+ # when in eheat mode (explicit on the thermostat), it will stage up to eh2 after 130s
710
+ # when in aux mode (thermostat set to heat; compressor at full capacity), it will stage up to eh2 after 310s
708
711
  19 => "FP1 (Cooling Liquid Line) Temperature",
709
712
  20 => "FP2",
710
713
  21 => "Condensate", # >= 270 normal, otherwise fault
@@ -867,11 +870,11 @@ module Aurora
867
870
  3225 => "VS Drive Details (Safemode 2)",
868
871
  3226 => "VS Drive Details (Alarm 1)",
869
872
  3227 => "VS Drive Details (Alarm 2)",
870
- 3327 => "VS Drive Temperature",
871
873
  3322 => "VS Drive Discharge Pressure",
872
874
  3323 => "VS Drive Suction Pressure",
873
875
  3325 => "VS Drive Discharge Temperature",
874
876
  3326 => "VS Drive Compressor Ambient Temperature",
877
+ 3327 => "VS Drive Temperature",
875
878
  3330 => "VS Drive Entering Water Temperature",
876
879
  3331 => "VS Drive Line Voltage",
877
880
  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)
@@ -44,14 +48,12 @@ module Aurora
44
48
  return unless (raw_value = HEATING_MODE.invert[value])
45
49
 
46
50
  @abc.modbus_slave.holding_registers[12_606] = raw_value
47
- @target_mode = value
48
51
  end
49
52
 
50
53
  def target_fan_mode=(value)
51
54
  return unless (raw_value = FAN_MODE.invert[value])
52
55
 
53
56
  @abc.modbus_slave.holding_registers[12_621] = raw_value
54
- @target_fan_mode = value
55
57
  end
56
58
 
57
59
  def heating_target_temperature=(value)
@@ -59,7 +61,6 @@ module Aurora
59
61
 
60
62
  raw_value = (value * 10).to_i
61
63
  @abc.modbus_slave.holding_registers[12_619] = raw_value
62
- @heating_target_temperature = value
63
64
  end
64
65
 
65
66
  def cooling_target_temperature=(value)
@@ -67,21 +68,18 @@ module Aurora
67
68
 
68
69
  raw_value = (value * 10).to_i
69
70
  @abc.modbus_slave.holding_registers[12_620] = raw_value
70
- @cooling_target_temperature = value
71
71
  end
72
72
 
73
73
  def fan_intermittent_on=(value)
74
74
  return unless value >= 0 && value <= 25 && (value % 5).zero?
75
75
 
76
76
  holding_registers[12_622] = value
77
- @fan_intermittent_on = value
78
77
  end
79
78
 
80
79
  def fan_intermittent_off=(value)
81
80
  return unless value >= 0 && value <= 40 && (value % 5).zero?
82
81
 
83
82
  holding_registers[12_623] = value
84
- @fan_intermittent_off = value
85
83
  end
86
84
  end
87
85
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aurora
4
- VERSION = "0.7.6"
4
+ VERSION = "1.0.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.7.6
4
+ version: 1.0.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: 2022-01-27 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