waterfurnace_aurora 0.8.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 288c8dbc14788d26473f14dcfaf388f75c8aee92c2f3b4d388bfa8326cfd2842
4
- data.tar.gz: c06f92936c81e402d08e01ae6b8c6c573acca46a85c9630914cb3c69f125a56a
3
+ metadata.gz: d9f0f7d0f0c846720df96f8889d1d5952ece7e0dbabea3431484f4efbff06f5a
4
+ data.tar.gz: e82bc4400c9543322debe51a21da1dea54d07a673f7302e5ec2a2910e8229714
5
5
  SHA512:
6
- metadata.gz: 5e64658afee912c8188af6d88bc98e208427b8dc4ef2ce1ea6ff1efcaca15ea2042ed4eef08ccfd764ec59ce0bb82013b0abd82173ec00f9ec61cbde07a66634
7
- data.tar.gz: 6deff29644f83c329f1727d0a568c87536618ae63c7a27e58e4054eff187496c6f04bf3b63e2c35e649b2a0eb5b6395d81450a68b0096d1cacee9137c2f53c31
6
+ metadata.gz: eddd8d24b22c6a1c1c64c8fdd29f79c00c76145252b5ddbcd08cf2c7edd0f03182439cb9cf511041bc1a789823b421eefcb3ac90d3f5c72e6fcb54fdf88dc60d
7
+ data.tar.gz: 76b06de5f2ff979a0c4eaaf05998d4856aa01f491f80fc224870920319d9abda0c89c800a221d532f3c2ccb850c01217570d3f72b447af221186da18ed81fa84
data/exe/aurora_fetch CHANGED
@@ -8,20 +8,31 @@ require "optparse"
8
8
  require "uri"
9
9
  require "yaml"
10
10
 
11
- debug_modbus = yaml = false
11
+ debug_modbus = yaml = ignore_missing_registers = false
12
12
  try_individual = nil
13
13
 
14
14
  options = OptionParser.new do |opts|
15
15
  opts.banner = "Usage: aurora_fetch /path/to/serial/port REGISTERS [options]"
16
16
 
17
17
  opts.separator("")
18
- opts.separator("Use `known` to fetch all identified registers. Use `valid` to fetch all registers that will respond.")
18
+ opts.separator(<<~TEXT)
19
+ Use `known` to fetch all identified registers. Use `valid` to fetch all registers
20
+ that should respond. Use `all` to search the entire ModBus address space. Note that
21
+ logging of current progress is only periodic, and does not log every register it's
22
+ trying to fetch.
23
+ TEXT
19
24
  opts.separator("")
20
25
 
21
- opts.on("--debug-modbus", "Print actual protocol bytes") { debug_modbus = true }
26
+ opts.on("--debug-modbus", "Print actual protocol bytes") do
27
+ debug_modbus = true
28
+ end
22
29
  opts.on("--[no-]try-individual",
23
30
  "Query registers one-by-one if a range has an illegal address. " \
24
31
  "Defaults to true for `valid` and `known` special registers, false otherwise.") { |v| try_individual = v }
32
+ opts.on("--ignore-missing-registers",
33
+ "For YAML input only, just log a warning when a register doesn't exist, instead of failing") do
34
+ ignore_missing_registers = true
35
+ end
25
36
  opts.on("-y", "--yaml", "Output raw values as YAML") { yaml = true }
26
37
  opts.on("-v", "--version", "Print version") do
27
38
  puts Aurora::VERSION
@@ -40,11 +51,11 @@ unless ARGV.length == 2
40
51
  exit 1
41
52
  end
42
53
 
43
- modbus_slave = Aurora::ABCClient.open_modbus_slave(ARGV[0])
54
+ modbus_slave = Aurora::ABCClient.open_modbus_slave(ARGV[0], ignore_missing_registers: ignore_missing_registers)
44
55
  modbus_slave.read_retry_timeout = 15
45
56
  modbus_slave.read_retries = 2
46
- modbus_slave.logger = Logger.new($stdout)
47
- modbus_slave.logger.level = debug_modbus ? :debug : :warn
57
+ Aurora.logger = modbus_slave.logger = Logger.new($stderr)
58
+ modbus_slave.logger.level = debug_modbus ? :debug : :info
48
59
 
49
60
  registers = Aurora::ABCClient.query_registers(modbus_slave, ARGV[1], try_individual: try_individual)
50
61
 
@@ -90,29 +90,33 @@ class MQTTBridge
90
90
  loop do
91
91
  begin
92
92
  @mutex.synchronize do
93
- @abc.refresh
94
-
95
- components = { @homie_abc => @abc,
96
- @compressor => @abc.compressor,
97
- @blower => @abc.blower,
98
- @pump => @abc.pump,
99
- @dhw => @abc.dhw,
100
- @humidistat => @abc.humidistat }.compact
101
- @abc.zones.each_with_index do |z, idx|
102
- homie_zone = @homie["zone#{idx + 1}"]
103
- components[homie_zone] = z
104
- end
93
+ @homie.mqtt.batch_publish do
94
+ @abc.refresh
95
+
96
+ components = { @homie_abc => @abc,
97
+ @aux_heat => @abc.aux_heat,
98
+ @compressor => @abc.compressor,
99
+ @blower => @abc.blower,
100
+ @pump => @abc.pump,
101
+ @dhw => @abc.dhw,
102
+ @humidistat => @abc.humidistat }.compact
103
+ @abc.zones.each_with_index do |z, idx|
104
+ homie_zone = @homie["zone#{idx + 1}"]
105
+ components[homie_zone] = z
106
+ end
105
107
 
106
- components.each do |(node, object)|
107
- node.each do |property|
108
- property.value = object.public_send(property.id.tr("-", "_"))
108
+ components.each do |(node, object)|
109
+ node.each do |property|
110
+ property.value = object.public_send(property.id.tr("-", "_"))
111
+ end
109
112
  end
110
- end
111
113
 
112
- @abc.faults.each_with_index do |fault_count, i|
113
- next if fault_count == 0xffff
114
+ @faults["current"].value = @abc.current_fault
115
+ @abc.faults.each_with_index do |fault_count, i|
116
+ next if fault_count == 0xffff
114
117
 
115
- @faults["e#{i + 1}"].value = fault_count
118
+ @faults["e#{i + 1}"].value = fault_count
119
+ end
116
120
  end
117
121
  end
118
122
  rescue => e
@@ -131,7 +135,7 @@ class MQTTBridge
131
135
  }
132
136
 
133
137
  @homie_abc = @homie.node("abc", "Heat Pump", "ABC") do |node|
134
- allowed_modes = %w[lockout standby blower heating cooling eh1 eh2 emergency waiting]
138
+ allowed_modes = %w[lockout standby blower heating heating_with_aux emergency_heat cooling waiting]
135
139
  allowed_modes << "dehumidify" if @abc.compressor.is_a?(Aurora::Compressor::VSDrive)
136
140
  node.property("current-mode",
137
141
  "Current Heating/Cooling Mode",
@@ -139,6 +143,31 @@ class MQTTBridge
139
143
  @abc.current_mode,
140
144
  format: allowed_modes,
141
145
  hass: { sensor: { state_class: :measurement } })
146
+ node.property("emergency-shutdown",
147
+ "Emergency Shutdown Requested",
148
+ :boolean,
149
+ @abc.emergency_shutdown?,
150
+ hass: :binary_sensor)
151
+ node.property("load-shed",
152
+ "Load Shed Requested",
153
+ :boolean,
154
+ @abc.load_shed?,
155
+ hass: :binary_sensor)
156
+ node.property("locked-out",
157
+ "Is the heat pump currently locked out?",
158
+ :boolean,
159
+ @abc.locked_out?,
160
+ hass: :binary_sensor)
161
+ node.property("derated",
162
+ "Is the compressor currently running at a derated level?",
163
+ :boolean,
164
+ @abc.derated?,
165
+ hass: :binary_sensor)
166
+ node.property("safe-mode",
167
+ "Is the heat pump currently in safe mode?",
168
+ :boolean,
169
+ @abc.safe_mode?,
170
+ hass: :binary_sensor)
142
171
  node.property("entering-air-temperature",
143
172
  "Entering Air Temperature",
144
173
  :float,
@@ -155,6 +184,17 @@ class MQTTBridge
155
184
  hass: { sensor: { device_class: :temperature,
156
185
  state_class: :measurement,
157
186
  entity_category: :diagnostic } })
187
+ node.property("low-pressure-switch",
188
+ "Low Pressure Switch Status",
189
+ :enum,
190
+ @abc.low_pressure_switch,
191
+ format: %w[open closed])
192
+ node.property("high-pressure-switch",
193
+ "High Pressure Switch Status",
194
+ :enum,
195
+ @abc.high_pressure_switch,
196
+ format: %w[open closed])
197
+
158
198
  if @abc.awl_communicating?
159
199
  node.property("leaving-air-temperature",
160
200
  "Leaving Air Temperature",
@@ -189,31 +229,22 @@ class MQTTBridge
189
229
  :integer,
190
230
  @abc.line_voltage,
191
231
  format: 90..635,
192
- unit: "V") do |value, property|
193
- @mutex.synchronize { property.value = @abc.line_voltage = value }
232
+ unit: "V") do |value|
233
+ @mutex.synchronize { @abc.line_voltage = value }
194
234
  end
195
- node.property("fp1",
196
- "FP1 Sensor",
235
+ node.property("air-coil-temperature",
236
+ "Air Coil Temperature (FP2)",
197
237
  :float,
198
- @abc.fp1,
238
+ @abc.air_coil_temperature,
199
239
  unit: "°F",
200
240
  hass: { sensor: { device_class: :temperature,
201
241
  state_class: :measurement,
202
242
  entity_category: :diagnostic } })
203
- node.property("fp2",
204
- "FP2 Sensor",
205
- :float,
206
- @abc.fp2,
207
- unit: "°F",
208
- hass: { sensor: { device_class: :temperature,
209
- state_class: :measurement,
210
- entity_category: :diagnostic } })
211
- %i[aux_heat total].each do |component|
212
- component = "#{component}_watts"
213
- node.property(component.tr("_", "-"),
214
- component.sub("_watts", " Power Usage").tr("_", " ").titleize,
243
+ if @abc.energy_monitoring?
244
+ node.property("watts",
245
+ "Total Power Usage",
215
246
  :integer,
216
- @abc.public_send(component),
247
+ @abc.watts,
217
248
  unit: "W",
218
249
  hass: { sensor: { device_class: :power,
219
250
  state_class: :measurement } })
@@ -227,6 +258,22 @@ class MQTTBridge
227
258
  @abc.compressor.speed,
228
259
  format: @abc.compressor.speed_range,
229
260
  hass: { sensor: { state_class: :measurement } })
261
+ node.property("cooling-liquid-line-temperature",
262
+ "Cooling Liquid Line Temperature (FP1)",
263
+ :float,
264
+ @abc.compressor.cooling_liquid_line_temperature,
265
+ unit: "°F",
266
+ hass: { sensor: { device_class: :temperature,
267
+ state_class: :measurement,
268
+ entity_category: :diagnostic } })
269
+ node.property("saturated-condensor-discharge-temperature",
270
+ "Saturated Condensor Discharge Temperature",
271
+ :float,
272
+ @abc.compressor.saturated_condensor_discharge_temperature,
273
+ unit: "°F",
274
+ hass: { sensor: { device_class: :temperature,
275
+ state_class: :measurement,
276
+ entity_category: :diagnostic } })
230
277
  if @abc.energy_monitoring?
231
278
  node.property("watts",
232
279
  "Power Usage",
@@ -247,6 +294,86 @@ class MQTTBridge
247
294
  hass: { sensor: { device_class: :temperature,
248
295
  state_class: :measurement,
249
296
  entity_category: :diagnostic } })
297
+ node.property("drive-temperature",
298
+ "Drive Temperature",
299
+ :float,
300
+ @abc.compressor.drive_temperature,
301
+ unit: "°F",
302
+ hass: { sensor: { device_class: :temperature,
303
+ state_class: :measurement,
304
+ entity_category: :diagnostic } })
305
+ node.property("inverter-temperature",
306
+ "Inverter Temperature",
307
+ :float,
308
+ @abc.compressor.inverter_temperature,
309
+ unit: "°F",
310
+ hass: { sensor: { device_class: :temperature,
311
+ state_class: :measurement,
312
+ entity_category: :diagnostic } })
313
+ node.property("fan-speed",
314
+ "Fan Speed",
315
+ :integer,
316
+ @abc.compressor.fan_speed,
317
+ unit: "%",
318
+ format: 0..100,
319
+ hass: { sensor: { state_class: :measurement,
320
+ entity_category: :diagnostic } })
321
+ node.property("discharge-temperature",
322
+ "Discharge Temperature",
323
+ :float,
324
+ @abc.compressor.discharge_temperature,
325
+ unit: "°F",
326
+ hass: { sensor: { device_class: :temperature,
327
+ state_class: :measurement,
328
+ entity_category: :diagnostic } })
329
+ node.property("discharge-pressure",
330
+ "Discharge Pressure",
331
+ :float,
332
+ @abc.compressor.discharge_pressure,
333
+ unit: "psi",
334
+ hass: { sensor: { device_class: :pressure,
335
+ state_class: :measurement,
336
+ entity_category: :diagnostic } })
337
+ node.property("suction-temperature",
338
+ "Suction Temperature",
339
+ :float,
340
+ @abc.compressor.suction_temperature,
341
+ unit: "°F",
342
+ hass: { sensor: { device_class: :temperature,
343
+ state_class: :measurement,
344
+ entity_category: :diagnostic } })
345
+ node.property("suction-pressure",
346
+ "Suction Pressure",
347
+ :float,
348
+ @abc.compressor.suction_pressure,
349
+ unit: "psi",
350
+ hass: { sensor: { device_class: :pressure,
351
+ state_class: :measurement,
352
+ entity_category: :diagnostic } })
353
+ node.property("saturated-evaporator-discharge-temperature",
354
+ "Saturated Evaporator Discharge Temperature",
355
+ :float,
356
+ @abc.compressor.saturated_evaporator_discharge_temperature,
357
+ unit: "°F",
358
+ hass: { sensor: { device_class: :temperature,
359
+ state_class: :measurement,
360
+ entity_category: :diagnostic } })
361
+ node.property("superheat-temperature",
362
+ "SuperHeat Temperature",
363
+ :float,
364
+ @abc.compressor.superheat_temperature,
365
+ unit: "°F",
366
+ hass: { sensor: { device_class: :temperature,
367
+ state_class: :measurement,
368
+ entity_category: :diagnostic } })
369
+ node.property("superheat-percentage",
370
+ "SuperHeat Percentage",
371
+ :integer,
372
+ @abc.compressor.superheat_percentage,
373
+ unit: "%",
374
+ format: 0..100,
375
+ hass: { sensor: { state_class: :measurement,
376
+ entity_category: :diagnostic } })
250
377
 
251
378
  next unless @abc.iz2?
252
379
 
@@ -259,6 +386,25 @@ class MQTTBridge
259
386
  entity_category: :diagnostic } })
260
387
  end
261
388
 
389
+ @aux_heat = @homie.node("aux-heat", "Aux Heater", "Aux Heater") do |node|
390
+ node.property("stage",
391
+ "Current Stage",
392
+ :integer,
393
+ @abc.aux_heat.stage,
394
+ format: 0..2,
395
+ hass: { sensor: { state_class: :measurement } })
396
+
397
+ if @abc.energy_monitoring?
398
+ node.property("watts",
399
+ "Power Usage",
400
+ :integer,
401
+ @abc.aux_heat.watts,
402
+ unit: "W",
403
+ hass: { sensor: { device_class: :power,
404
+ state_class: :measurement } })
405
+ end
406
+ end
407
+
262
408
  @blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
263
409
  node.property("running",
264
410
  "Running",
@@ -294,8 +440,8 @@ class MQTTBridge
294
440
  :integer,
295
441
  @abc.blower.public_send(field),
296
442
  format: 1..12,
297
- hass: { number: { entity_category: :config } }) do |value, property|
298
- @mutex.synchronize { property.value = @abc.blower.public_send("#{field}=", value) }
443
+ hass: { number: { entity_category: :config } }) do |value|
444
+ @mutex.synchronize { @abc.blower.public_send("#{field}=", value) }
299
445
  end
300
446
  end
301
447
 
@@ -340,8 +486,8 @@ class MQTTBridge
340
486
  "Manual Control",
341
487
  :boolean,
342
488
  @abc.pump.manual_control?,
343
- hass: { switch: { entity_category: :diagnostic } }) do |value, property|
344
- @mutex.synchronize { property.value = @abc.pump.manual_control = value }
489
+ hass: { switch: { entity_category: :diagnostic } }) do |value|
490
+ @mutex.synchronize { @abc.pump.manual_control = value }
345
491
  end
346
492
  node.property("minimum-speed",
347
493
  "Actual Minimum Speed",
@@ -349,8 +495,8 @@ class MQTTBridge
349
495
  @abc.pump.minimum_speed,
350
496
  format: 0..100,
351
497
  unit: "%",
352
- hass: { number: { entity_category: :config } }) do |value, property|
353
- @mutex.synchronize { property.value = @abc.pump.minimum_speed = value }
498
+ hass: { number: { entity_category: :config } }) do |value|
499
+ @mutex.synchronize { @abc.pump.minimum_speed = value }
354
500
  end
355
501
  node.property("maximum-speed",
356
502
  "Actual Maximum Speed",
@@ -358,8 +504,8 @@ class MQTTBridge
358
504
  @abc.pump.minimum_speed,
359
505
  format: 0..100,
360
506
  unit: "%",
361
- hass: { number: { entity_category: :config } }) do |value, property|
362
- @mutex.synchronize { property.value = @abc.pump.maximum_speed = value }
507
+ hass: { number: { entity_category: :config } }) do |value|
508
+ @mutex.synchronize { @abc.pump.maximum_speed = value }
363
509
  end
364
510
  next unless @abc.awl_axb?
365
511
 
@@ -369,8 +515,8 @@ class MQTTBridge
369
515
  @abc.pump.speed,
370
516
  format: 0..100,
371
517
  unit: "%",
372
- hass: { number: { entity_category: :diagnostic } }) do |value, property|
373
- @mutex.synchronize { property.value = @abc.pump.speed = value }
518
+ hass: { number: { entity_category: :diagnostic } }) do |value|
519
+ @mutex.synchronize { @abc.pump.speed = value }
374
520
  end
375
521
  end
376
522
 
@@ -380,8 +526,8 @@ class MQTTBridge
380
526
  "Enabled",
381
527
  :boolean,
382
528
  @abc.dhw.enabled,
383
- hass: { switch: { icon: "mdi:water-boiler" } }) do |value, property|
384
- @mutex.synchronize { property.value = @abc.dhw.enabled = value }
529
+ hass: { switch: { icon: "mdi:water-boiler" } }) do |value|
530
+ @mutex.synchronize { @abc.dhw.enabled = value }
385
531
  end
386
532
  node.property("running",
387
533
  "Pump Running",
@@ -401,8 +547,8 @@ class MQTTBridge
401
547
  @abc.dhw.set_point,
402
548
  format: 100..140,
403
549
  unit: "°F",
404
- hass: { number: { step: 5 } }) do |value, property|
405
- @mutex.synchronize { property.value = @abc.dhw.set_point = value }
550
+ hass: { number: { step: 5 } }) do |value|
551
+ @mutex.synchronize { @abc.dhw.set_point = value }
406
552
  end
407
553
  end
408
554
  end
@@ -418,16 +564,16 @@ class MQTTBridge
418
564
  "Humidifier Mode",
419
565
  :enum,
420
566
  @abc.humidistat.humidifier_mode,
421
- format: %i[auto manual]) do |value, property|
422
- @mutex.synchronize { property.value = @abc.humidistat.humidifier_mode = value.to_sym }
567
+ format: %i[auto manual]) do |value|
568
+ @mutex.synchronize { @abc.humidistat.humidifier_mode = value.to_sym }
423
569
  end
424
570
  node.property("humidification-target",
425
571
  "Humidification Target Relative Humidity",
426
572
  :integer,
427
573
  @abc.humidistat.humidification_target,
428
574
  unit: "%",
429
- format: 15..50) do |value, property|
430
- @mutex.synchronize { property.value = @abc.humidistat.humidification_target = value }
575
+ format: 15..50) do |value|
576
+ @mutex.synchronize { @abc.humidistat.humidification_target = value }
431
577
  end
432
578
  node.hass_humidifier("humidifier-running",
433
579
  target_property: "humidification-target",
@@ -450,16 +596,16 @@ class MQTTBridge
450
596
  "Dehumidifier Mode",
451
597
  :enum,
452
598
  @abc.humidistat.dehumidifier_mode,
453
- format: %i[auto manual]) do |value, property|
454
- @mutex.synchronize { property.value = @abc.humidistat.dehumidifier_mode = value.to_sym }
599
+ format: %i[auto manual]) do |value|
600
+ @mutex.synchronize { @abc.humidistat.dehumidifier_mode = value.to_sym }
455
601
  end
456
602
  node.property("dehumidification-target",
457
603
  "Dehumidification Target Relative Humidity",
458
604
  :integer,
459
605
  @abc.humidistat.dehumidification_target,
460
606
  unit: "%",
461
- format: 35..65) do |value, property|
462
- @mutex.synchronize { property.value = @abc.humidistat.dehumidification_target = value }
607
+ format: 35..65) do |value|
608
+ @mutex.synchronize { @abc.humidistat.dehumidification_target = value }
463
609
  end
464
610
  node.hass_humidifier("dehumidifier-running",
465
611
  target_property: "dehumidification-target",
@@ -482,6 +628,12 @@ class MQTTBridge
482
628
  end
483
629
 
484
630
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
631
+ node.property("current",
632
+ "Current fault",
633
+ :integer,
634
+ @abc.current_fault,
635
+ format: 0..99,
636
+ hass: :sensor)
485
637
  node.property("clear-history",
486
638
  "Reset Fault Counts",
487
639
  :enum,
@@ -520,15 +672,15 @@ class MQTTBridge
520
672
  "Target Heating/Cooling Mode",
521
673
  :enum,
522
674
  zone.target_mode,
523
- format: allowed_modes) do |value, property|
524
- @mutex.synchronize { property.value = zone.target_mode = value.to_sym }
675
+ format: allowed_modes) do |value|
676
+ @mutex.synchronize { zone.target_mode = value.to_sym }
525
677
  end
526
678
  node.property("target-fan-mode",
527
679
  "Target Fan Mode",
528
680
  :enum,
529
681
  zone.target_fan_mode,
530
- format: %w[auto continuous intermittent]) do |value, property|
531
- @mutex.synchronize { property.value = zone.target_fan_mode = value.to_sym }
682
+ format: %w[auto continuous intermittent]) do |value|
683
+ @mutex.synchronize { zone.target_fan_mode = value.to_sym }
532
684
  end
533
685
  node.property("fan-intermittent-on",
534
686
  "Fan Intermittent Mode On Duration",
@@ -536,8 +688,8 @@ class MQTTBridge
536
688
  zone.fan_intermittent_on,
537
689
  unit: "M",
538
690
  format: %w[0 5 10 15 20],
539
- hass: { select: { entity_category: :config } }) do |value, property|
540
- @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
691
+ hass: { select: { entity_category: :config } }) do |value|
692
+ @mutex.synchronize { zone.fan_intermittent_on = value.to_i }
541
693
  end
542
694
  node.property("fan-intermittent-off",
543
695
  "Fan Intermittent Mode Off Duration",
@@ -545,8 +697,8 @@ class MQTTBridge
545
697
  zone.fan_intermittent_on,
546
698
  unit: "M",
547
699
  format: %w[0 5 10 15 20 25 30 35 40],
548
- hass: { select: { entity_category: :config } }) do |value, property|
549
- @mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
700
+ hass: { select: { entity_category: :config } }) do |value|
701
+ @mutex.synchronize { zone.fan_intermittent_on = value.to_i }
550
702
  end
551
703
  node.property("current-fan-mode",
552
704
  "Current Fan Status",
@@ -587,15 +739,15 @@ class MQTTBridge
587
739
  :integer,
588
740
  zone.heating_target_temperature,
589
741
  unit: "°F",
590
- format: 40..90) do |value, property|
591
- @mutex.synchronize { property.value = zone.heating_target_temperature = value }
742
+ format: 40..90) do |value|
743
+ @mutex.synchronize { zone.heating_target_temperature = value }
592
744
  end
593
745
  node.property("cooling-target-temperature",
594
746
  "Cooling Target Temperature", :integer,
595
747
  zone.cooling_target_temperature,
596
748
  unit: "°F",
597
- format: 54..99) do |value, property|
598
- @mutex.synchronize { property.value = zone.cooling_target_temperature = value }
749
+ format: 54..99) do |value|
750
+ @mutex.synchronize { zone.cooling_target_temperature = value }
599
751
  end
600
752
  node.hass_climate(action_property: "current-mode",
601
753
  current_temperature_property: "ambient-temperature",
@@ -626,12 +778,12 @@ class MQTTBridge
626
778
  end
627
779
 
628
780
  log_level = ARGV.include?("--debug") ? :debug : :warn
629
- logger = Logger.new($stdout)
630
- logger.level = log_level
631
- abc.modbus_slave.logger = logger
781
+ Aurora.logger = Logger.new($stdout)
782
+ Aurora.logger.level = log_level
783
+ abc.modbus_slave.logger = Aurora.logger
632
784
 
633
785
  device = "aurora-#{abc.serial_number}"
634
786
  homie = MQTT::Homie::Device.new(device, "WaterFurnace", mqtt: mqtt_uri)
635
- homie.logger = logger
787
+ homie.logger = Aurora.logger
636
788
 
637
789
  MQTTBridge.new(abc, homie)
data/exe/web_aid_tool CHANGED
@@ -7,13 +7,17 @@ require "logger"
7
7
  require "optparse"
8
8
  require "yaml"
9
9
 
10
- debug_modbus = monitor = false
10
+ debug_modbus = monitor = ignore_missing_registers = false
11
11
 
12
12
  options = OptionParser.new do |opts|
13
13
  opts.banner = "Usage: web_aid_tool /path/to/serial/port [options]"
14
14
 
15
15
  opts.on("--debug-modbus", "Print actual protocol bytes") { debug_modbus = true }
16
16
  opts.on("--monitor", "Print interpreted registers as they are requested, like aurora_monitor") { monitor = true }
17
+ opts.on("--ignore-missing-registers",
18
+ "For YAML input only, just log a warning when a register doesn't exist, instead of failing") do
19
+ ignore_missing_registers = true
20
+ end
17
21
  opts.on("-v", "--version", "Print version") do
18
22
  puts Aurora::VERSION
19
23
  exit
@@ -31,7 +35,7 @@ unless ARGV.length == 1
31
35
  exit 1
32
36
  end
33
37
 
34
- slave = Aurora::ABCClient.open_modbus_slave(ARGV[0])
38
+ slave = Aurora::ABCClient.open_modbus_slave(ARGV[0], ignore_missing_registers: ignore_missing_registers)
35
39
  slave.logger = Logger.new($stdout)
36
40
  slave.logger.level = debug_modbus ? :debug : :warn
37
41
 
@@ -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"
@@ -14,7 +15,7 @@ require "aurora/thermostat"
14
15
  module Aurora
15
16
  class ABCClient
16
17
  class << self
17
- def open_modbus_slave(uri)
18
+ def open_modbus_slave(uri, ignore_missing_registers: false)
18
19
  uri = URI.parse(uri)
19
20
 
20
21
  io = case uri.scheme
@@ -31,7 +32,10 @@ module Aurora
31
32
  require "aurora/mqtt_modbus"
32
33
  return Aurora::MQTTModBus.new(uri)
33
34
  else
34
- return Aurora::MockABC.new(YAML.load_file(uri.path)) if File.file?(uri.path)
35
+ if File.file?(uri.path)
36
+ return Aurora::MockABC.new(YAML.load_file(uri.path),
37
+ ignore_missing_registers: ignore_missing_registers)
38
+ end
35
39
 
36
40
  require "ccutrer-serialport"
37
41
  CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)
@@ -53,6 +57,10 @@ module Aurora
53
57
  implicit = true
54
58
  try_individual = true if try_individual.nil?
55
59
  break Aurora::REGISTER_RANGES
60
+ when "all"
61
+ implicit = true
62
+ try_individual = true if try_individual.nil?
63
+ break 0..65_535
56
64
  when /^(\d+)(?:\.\.|-)(\d+)$/
57
65
  $1.to_i..$2.to_i
58
66
  else
@@ -61,28 +69,48 @@ module Aurora
61
69
  end
62
70
  queries = Aurora.normalize_ranges(ranges)
63
71
  registers = {}
72
+ last_log_time = nil
64
73
  queries.each do |subquery|
74
+ last_log_time = log_query(last_log_time, subquery.inspect)
65
75
  registers.merge!(modbus_slave.read_multiple_holding_registers(*subquery))
66
- rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
76
+ rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction, ::ModBus::Errors::ModBusTimeout
67
77
  # maybe this unit doesn't respond to all the addresses we want?
68
78
  raise unless implicit
69
79
 
70
80
  # try each query individually
71
81
  subquery.each do |subsubquery|
82
+ last_log_time = log_query(last_log_time, subsubquery.inspect)
72
83
  registers.merge!(modbus_slave.read_multiple_holding_registers(subsubquery))
73
- rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
84
+ rescue ::ModBus::Errors::IllegalDataAddress,
85
+ ::ModBus::Errors::IllegalFunction,
86
+ ::ModBus::Errors::ModBusTimeout => e
87
+ raise if e.is_a?(::ModBus::Errors::ModBusTimeout) && !try_individual
74
88
  next unless try_individual
75
89
 
76
90
  # seriously?? try each register individually
77
- subsubquery.each do |i|
91
+ Array(subsubquery).each do |i|
92
+ last_log_time = log_query(last_log_time, i.to_s)
78
93
  registers[i] = modbus_slave.holding_registers[i]
79
94
  rescue ::ModBus::Errors::IllegalDataAddress, ::ModBus::Errors::IllegalFunction
95
+ # don't catch ModBusTimeout here... it should have no problem responding to a single register request
80
96
  next
81
97
  end
82
98
  end
83
99
  end
84
100
  registers
85
101
  end
102
+
103
+ private
104
+
105
+ def log_query(last_log_time, query)
106
+ last_log_time ||= Process.clock_gettime(Process::CLOCK_MONOTONIC)
107
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
108
+ if now - last_log_time > 5
109
+ Aurora&.logger&.info("Fetching register(s) #{query}...")
110
+ last_log_time = now
111
+ end
112
+ last_log_time
113
+ end
86
114
  end
87
115
 
88
116
  attr_reader :modbus_slave,
@@ -90,23 +118,36 @@ module Aurora
90
118
  :model,
91
119
  :serial_number,
92
120
  :zones,
121
+ :aux_heat,
93
122
  :compressor,
94
123
  :blower,
95
124
  :pump,
96
125
  :dhw,
97
126
  :humidistat,
127
+ :current_fault,
98
128
  :faults,
129
+ :locked_out,
130
+ :derated,
131
+ :safe_mode,
99
132
  :current_mode,
133
+ :low_pressure_switch,
134
+ :high_pressure_switch,
135
+ :emergency_shutdown,
136
+ :load_shed,
100
137
  :entering_air_temperature,
101
138
  :leaving_air_temperature,
102
139
  :leaving_water_temperature,
103
140
  :entering_water_temperature,
104
141
  :outdoor_temperature,
105
- :fp1,
106
- :fp2,
142
+ :air_coil_temperature,
107
143
  :line_voltage,
108
- :aux_heat_watts,
109
- :total_watts
144
+ :watts
145
+
146
+ alias_method :emergency_shutdown?, :emergency_shutdown
147
+ alias_method :load_shed?, :load_shed
148
+ alias_method :locked_out?, :locked_out
149
+ alias_method :derated?, :derated
150
+ alias_method :safe_mode?, :safe_mode
110
151
 
111
152
  def initialize(uri)
112
153
  @modbus_slave = self.class.open_modbus_slave(uri)
@@ -129,6 +170,7 @@ module Aurora
129
170
 
130
171
  @abc_dipswitches = registers[33]
131
172
  @axb_dipswitches = registers[1103]
173
+ @aux_heat = AuxHeat.new(self)
132
174
  @compressor = if @program == "ABCVSP"
133
175
  Compressor::VSDrive.new(self)
134
176
  else
@@ -154,13 +196,15 @@ module Aurora
154
196
 
155
197
  @faults = []
156
198
 
157
- @registers_to_read = [6, 19..20, 25, 30, 112, 344, 567, 1104, 1110..1111, 1114, 1150..1153, 1165]
199
+ @entering_air_register = awl_axb? ? 740 : 567
200
+ @registers_to_read = [6, 19..20, 25, 30..31, 112, 344, @entering_air_register, 1104, 1110..1111, 1114, 1150..1153,
201
+ 1165]
158
202
  @registers_to_read.concat([741, 31_003]) if awl_communicating?
159
203
  @registers_to_read << 900 if awl_axb?
160
204
  zones.each do |z|
161
205
  @registers_to_read.concat(z.registers_to_read)
162
206
  end
163
- @components = [compressor, blower, pump, dhw, humidistat].compact
207
+ @components = [aux_heat, compressor, blower, pump, dhw, humidistat].compact
164
208
  @components.each do |component|
165
209
  @registers_to_read.concat(component.registers_to_read)
166
210
  end
@@ -181,34 +225,40 @@ module Aurora
181
225
 
182
226
  outputs = registers[30]
183
227
 
184
- @entering_air_temperature = registers[567]
228
+ @entering_air_temperature = registers[@entering_air_register]
185
229
  @leaving_air_temperature = registers[900] if awl_axb?
186
230
  @leaving_water_temperature = registers[1110]
187
231
  @entering_water_temperature = registers[1111]
188
232
  @outdoor_temperature = registers[31_003]
189
- @fp1 = registers[19]
190
- @fp2 = registers[20]
191
- @locked_out = registers[25] & 0x8000
192
- @error = registers[25] & 0x7fff
193
- @derated = (41..46).cover?(@error)
194
- @safe_mode = [47, 48, 49, 72, 74].include?(@error)
233
+ @air_coil_temperature = registers[20]
234
+ @locked_out = !(registers[25] & 0x8000).zero?
235
+ @current_fault = registers[25] & 0x7fff
236
+ @derated = (41..46).cover?(@current_fault)
237
+ @safe_mode = [47, 48, 49, 72, 74].include?(@current_fault)
238
+ @low_pressure_switch = registers[31][:lps]
239
+ @high_pressure_switch = registers[31][:hps]
240
+ @emergency_shutdown = !!registers[31][:emergency_shutdown]
241
+ @load_shed = !!registers[31][:load_shed]
195
242
  @line_voltage = registers[112]
196
- @aux_heat_watts = registers[1151]
197
- @total_watts = registers[1153]
243
+ @watts = registers[1153]
198
244
 
199
245
  @current_mode = if outputs.include?(:lockout)
200
246
  :lockout
201
247
  elsif registers[362]
202
248
  :dehumidify
203
249
  elsif outputs.include?(:cc2) || outputs.include?(:cc)
204
- outputs.include?(:rv) ? :cooling : :heating
205
- elsif outputs.include?(:eh2)
206
- outputs.include?(:rv) ? :eh2 : :emergency
207
- elsif outputs.include?(:eh1)
208
- outputs.include?(:rv) ? :eh1 : :emergency
250
+ if outputs.include?(:rv)
251
+ :cooling
252
+ elsif outputs.include?(:eh2) || outputs.include?(:eh1)
253
+ :heating_with_aux
254
+ else
255
+ :heating
256
+ end
257
+ elsif outputs.include?(:eh2) || outputs.include?(:eh1)
258
+ :emergency_heat
209
259
  elsif outputs.include?(:blower)
210
260
  :blower
211
- elsif registers[6]
261
+ elsif !registers[6].zero?
212
262
  :waiting
213
263
  else
214
264
  :standby
@@ -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}"
@@ -5,7 +5,7 @@ require "aurora/component"
5
5
  module Aurora
6
6
  module Compressor
7
7
  class GenericCompressor < Component
8
- attr_reader :speed, :watts
8
+ attr_reader :speed, :watts, :cooling_liquid_line_temperature, :saturated_condensor_discharge_temperature
9
9
 
10
10
  def initialize(abc, stages)
11
11
  super(abc)
@@ -21,11 +21,9 @@ module Aurora
21
21
  end
22
22
 
23
23
  def registers_to_read
24
- if abc.energy_monitoring?
25
- [1146..1147]
26
- else
27
- []
28
- end
24
+ result = [19, 1134]
25
+ result << (1146..1147) if abc.energy_monitoring?
26
+ result
29
27
  end
30
28
 
31
29
  def refresh(registers)
@@ -37,12 +35,25 @@ module Aurora
37
35
  else
38
36
  0
39
37
  end
38
+ @cooling_liquid_line_temperature = registers[19]
39
+ @saturated_condensor_discharge_temperature = registers[1134]
40
40
  @watts = registers[1146] if abc.energy_monitoring?
41
41
  end
42
42
  end
43
43
 
44
44
  class VSDrive < GenericCompressor
45
- attr_reader :ambient_temperature, :iz2_desired_speed
45
+ attr_reader :drive_temperature,
46
+ :inverter_temperature,
47
+ :ambient_temperature,
48
+ :iz2_desired_speed,
49
+ :fan_speed,
50
+ :discharge_pressure,
51
+ :discharge_temperature,
52
+ :suction_pressure,
53
+ :suction_temperature,
54
+ :saturated_evaporator_discharge_temperature,
55
+ :superheat_temperature,
56
+ :superheat_percentage
46
57
 
47
58
  def initialize(abc)
48
59
  super(abc, 12)
@@ -53,7 +64,7 @@ module Aurora
53
64
  end
54
65
 
55
66
  def registers_to_read
56
- result = super + [209, 3001, 3326]
67
+ result = super + [209, 3001, 3322..3327, 3522, 3524, 3808, 3903..3906]
57
68
  result << 564 if abc.iz2?
58
69
  result
59
70
  end
@@ -62,7 +73,18 @@ module Aurora
62
73
  super
63
74
 
64
75
  @speed = registers[3001]
76
+ @discharge_pressure = registers[3322]
77
+ @suction_pressure = registers[3323]
78
+ @discharge_temperature = registers[3325]
65
79
  @ambient_temperature = registers[3326]
80
+ @drive_temperature = registers[3327]
81
+ @inverter_temperature = registers[3522]
82
+ @fan_speed = registers[3524]
83
+ @superheat_percentage = registers[3808]
84
+ @suction_temperature = registers[3903]
85
+ @saturated_evaporator_discharge_temperature = registers[3905]
86
+ @superheat_temperature = registers[3906]
87
+
66
88
  @iz2_desired_speed = registers[564] if abc.iz2?
67
89
  end
68
90
  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
@@ -75,8 +75,6 @@ module Aurora
75
75
  raw_value |= 0x4000 if humidifier_mode == :auto
76
76
  raw_value |= 0x8000 if dehumidifier_mode == :auto
77
77
  holding_registers[abc.iz2? ? 21_114 : 12_309] = raw_value
78
- @humidifier_mode = humidifier_mode
79
- @dehumidifier_mode = dehumidifier_mode
80
78
  end
81
79
 
82
80
  def humidification_target=(value)
@@ -92,8 +90,6 @@ module Aurora
92
90
  raise ArgumentError unless (35..65).cover?(dehumidification_target)
93
91
 
94
92
  holding_registers[abc.iz2? ? 21_115 : 12_310] = (humidification_target << 8) + dehumidification_target
95
- @humidification_target = humidification_target
96
- @dehumidification_target = dehumidification_target
97
93
  end
98
94
  end
99
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
@@ -4,7 +4,8 @@ module Aurora
4
4
  class MockABC
5
5
  attr_accessor :logger
6
6
 
7
- def initialize(registers)
7
+ def initialize(registers, ignore_missing_registers: false)
8
+ @ignore_missing_registers = ignore_missing_registers
8
9
  @registers = registers
9
10
  end
10
11
 
@@ -59,6 +60,8 @@ module Aurora
59
60
  private
60
61
 
61
62
  def missing_register(idx)
63
+ raise ::ModBus::Errors::IllegalDataAddress unless @ignore_missing_registers
64
+
62
65
  logger.warn("missing register #{idx}")
63
66
  end
64
67
  end
@@ -60,6 +60,7 @@ module Aurora
60
60
  end
61
61
 
62
62
  def to_int32(registers, idx)
63
+ Aurora&.logger&.warn("Missing register #{idx + 1}") unless registers[idx + 1]
63
64
  (registers[idx] << 16) + registers[idx + 1]
64
65
  end
65
66
 
@@ -229,6 +230,8 @@ module Aurora
229
230
  0x08 => :blower,
230
231
  0x10 => :eh1,
231
232
  0x20 => :eh2,
233
+ # 0x40 => ??, # this turns on and off quite a bit during normal operation
234
+ # 0x80 => ??, # this turns on occasionally during normal operation; I've only seen it when aux heat is on
232
235
  0x200 => :accessory,
233
236
  0x400 => :lockout,
234
237
  0x800 => :alarm
@@ -703,10 +706,11 @@ module Aurora
703
706
  9 => "Compressor Minimum Run Time",
704
707
  15 => "Blower Off Delay",
705
708
  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
- 19 => "FP1 (Cooling Liquid Line) Temperature",
709
- 20 => "FP2",
709
+ 17 => "Aux/E Heat Stage", # this is how long aux/eheat have been requested in seconds
710
+ # when in eheat mode (explicit on the thermostat), it will stage up to eh2 after 130s
711
+ # when in aux mode (thermostat set to heat; compressor at full capacity), it will stage up to eh2 after 310s
712
+ 19 => "Cooling Liquid Line Temperature (FP1)",
713
+ 20 => "Air Coil Temperature (FP2)",
710
714
  21 => "Condensate", # >= 270 normal, otherwise fault
711
715
  25 => "Last Fault Number", # high bit set if locked out
712
716
  26 => "Last Lockout",
@@ -867,11 +871,11 @@ module Aurora
867
871
  3225 => "VS Drive Details (Safemode 2)",
868
872
  3226 => "VS Drive Details (Alarm 1)",
869
873
  3227 => "VS Drive Details (Alarm 2)",
870
- 3327 => "VS Drive Temperature",
871
874
  3322 => "VS Drive Discharge Pressure",
872
875
  3323 => "VS Drive Suction Pressure",
873
876
  3325 => "VS Drive Discharge Temperature",
874
877
  3326 => "VS Drive Compressor Ambient Temperature",
878
+ 3327 => "VS Drive Temperature",
875
879
  3330 => "VS Drive Entering Water Temperature",
876
880
  3331 => "VS Drive Line Voltage",
877
881
  3332 => "VS Drive Thermo Power",
@@ -15,9 +15,9 @@ module Aurora
15
15
  :fan_intermittent_off
16
16
 
17
17
  def registers_to_read
18
- return [31] unless @abc.awl_thermostat?
18
+ return [] unless @abc.awl_thermostat?
19
19
 
20
- [31, 502, 745..746, 12_005..12_006]
20
+ [502, 745..746, 12_005..12_006]
21
21
  end
22
22
 
23
23
  def refresh(registers)
@@ -48,14 +48,12 @@ module Aurora
48
48
  return unless (raw_value = HEATING_MODE.invert[value])
49
49
 
50
50
  @abc.modbus_slave.holding_registers[12_606] = raw_value
51
- @target_mode = value
52
51
  end
53
52
 
54
53
  def target_fan_mode=(value)
55
54
  return unless (raw_value = FAN_MODE.invert[value])
56
55
 
57
56
  @abc.modbus_slave.holding_registers[12_621] = raw_value
58
- @target_fan_mode = value
59
57
  end
60
58
 
61
59
  def heating_target_temperature=(value)
@@ -63,7 +61,6 @@ module Aurora
63
61
 
64
62
  raw_value = (value * 10).to_i
65
63
  @abc.modbus_slave.holding_registers[12_619] = raw_value
66
- @heating_target_temperature = value
67
64
  end
68
65
 
69
66
  def cooling_target_temperature=(value)
@@ -71,21 +68,18 @@ module Aurora
71
68
 
72
69
  raw_value = (value * 10).to_i
73
70
  @abc.modbus_slave.holding_registers[12_620] = raw_value
74
- @cooling_target_temperature = value
75
71
  end
76
72
 
77
73
  def fan_intermittent_on=(value)
78
74
  return unless value >= 0 && value <= 25 && (value % 5).zero?
79
75
 
80
76
  holding_registers[12_622] = value
81
- @fan_intermittent_on = value
82
77
  end
83
78
 
84
79
  def fan_intermittent_off=(value)
85
80
  return unless value >= 0 && value <= 40 && (value % 5).zero?
86
81
 
87
82
  holding_registers[12_623] = value
88
- @fan_intermittent_off = value
89
83
  end
90
84
  end
91
85
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aurora
4
- VERSION = "0.8.0"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/aurora.rb CHANGED
@@ -15,4 +15,7 @@ ModBus::Client::Slave.prepend(Aurora::ModBus::Slave)
15
15
  ModBus::RTUSlave.prepend(Aurora::ModBus::RTU)
16
16
 
17
17
  module Aurora
18
+ class << self
19
+ attr_accessor :logger
20
+ end
18
21
  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.8.0
4
+ version: 1.2.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: 2022-01-27 00:00:00.000000000 Z
11
+ date: 2022-02-01 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