waterfurnace_aurora 0.7.7 → 0.8.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: 7af20433d8163a332a3d09c36b23af07b95d9beb088d4a0ce69436a54e46e434
4
- data.tar.gz: 29a7c84feb1f58ecb6b0a44538f1a530add179ea95b0285f1739f172730b2d24
3
+ metadata.gz: 288c8dbc14788d26473f14dcfaf388f75c8aee92c2f3b4d388bfa8326cfd2842
4
+ data.tar.gz: c06f92936c81e402d08e01ae6b8c6c573acca46a85c9630914cb3c69f125a56a
5
5
  SHA512:
6
- metadata.gz: 81c6604273350396388be239846997ee151f1b92ac47c270977e63347a5eeea81025eefece7d60cb3f2e374bca65816548bf3b8cc36bf249a1dbec57fbc0cb3a
7
- data.tar.gz: 75ac82e3784f556b13274d5de4e64889023963286a3787e2521fef74ca5fb19ff0c49ea40a35fef7ea95bcae80b463ff84d15e67e4ddce098ffd69a3ead95843
6
+ metadata.gz: 5e64658afee912c8188af6d88bc98e208427b8dc4ef2ce1ea6ff1efcaca15ea2042ed4eef08ccfd764ec59ce0bb82013b0abd82173ec00f9ec61cbde07a66634
7
+ data.tar.gz: 6deff29644f83c329f1727d0a568c87536618ae63c7a27e58e4054eff187496c6f04bf3b63e2c35e649b2a0eb5b6395d81450a68b0096d1cacee9137c2f53c31
@@ -155,14 +155,16 @@ class MQTTBridge
155
155
  hass: { sensor: { device_class: :temperature,
156
156
  state_class: :measurement,
157
157
  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 } })
158
+ if @abc.awl_communicating?
159
+ node.property("leaving-air-temperature",
160
+ "Leaving Air Temperature",
161
+ :float,
162
+ @abc.leaving_air_temperature,
163
+ unit: "°F",
164
+ hass: { sensor: { device_class: :temperature,
165
+ state_class: :measurement,
166
+ entity_category: :diagnostic } })
167
+ end
166
168
  node.property("leaving-water-temperature",
167
169
  "Leaving Water Temperature",
168
170
  :float,
@@ -171,7 +173,8 @@ class MQTTBridge
171
173
  hass: { sensor: { device_class: :temperature,
172
174
  state_class: :measurement,
173
175
  entity_category: :diagnostic } })
174
- unless @abc.outdoor_temperature.zero? # TODO: figure out the config if this actually exists
176
+ # TODO: figure out the config if this actually exists
177
+ if @abc.awl_communicating? && !@abc.outdoor_temperature.zero?
175
178
  node.property("outdoor-temperature",
176
179
  "Outdoor Temperature",
177
180
  :float,
@@ -333,15 +336,6 @@ class MQTTBridge
333
336
  :boolean,
334
337
  @abc.pump.running,
335
338
  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
339
  node.property("manual-control",
346
340
  "Manual Control",
347
341
  :boolean,
@@ -367,6 +361,17 @@ class MQTTBridge
367
361
  hass: { number: { entity_category: :config } }) do |value, property|
368
362
  @mutex.synchronize { property.value = @abc.pump.maximum_speed = value }
369
363
  end
364
+ next unless @abc.awl_axb?
365
+
366
+ node.property("speed",
367
+ "Speed",
368
+ :integer,
369
+ @abc.pump.speed,
370
+ format: 0..100,
371
+ unit: "%",
372
+ hass: { number: { entity_category: :diagnostic } }) do |value, property|
373
+ @mutex.synchronize { property.value = @abc.pump.speed = value }
374
+ end
370
375
  end
371
376
 
372
377
  if @abc.dhw
@@ -396,47 +401,41 @@ class MQTTBridge
396
401
  @abc.dhw.set_point,
397
402
  format: 100..140,
398
403
  unit: "°F",
399
- hass: :number) do |value, property|
404
+ hass: { number: { step: 5 } }) do |value, property|
400
405
  @mutex.synchronize { property.value = @abc.dhw.set_point = value }
401
406
  end
402
407
  end
403
408
  end
404
409
 
405
410
  @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
411
  if @abc.humidistat.humidifier?
415
412
  node.property("humidifier-running",
416
413
  "Humidifier Running",
417
414
  :boolean,
418
415
  @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 }
416
+ if @abc.awl_communicating?
417
+ node.property("humidifier-mode",
418
+ "Humidifier Mode",
419
+ :enum,
420
+ @abc.humidistat.humidifier_mode,
421
+ format: %i[auto manual]) do |value, property|
422
+ @mutex.synchronize { property.value = @abc.humidistat.humidifier_mode = value.to_sym }
423
+ end
424
+ node.property("humidification-target",
425
+ "Humidification Target Relative Humidity",
426
+ :integer,
427
+ @abc.humidistat.humidification_target,
428
+ unit: "%",
429
+ format: 15..50) do |value, property|
430
+ @mutex.synchronize { property.value = @abc.humidistat.humidification_target = value }
431
+ end
432
+ node.hass_humidifier("humidifier-running",
433
+ target_property: "humidification-target",
434
+ mode_property: "humidifier-mode",
435
+ id: "humidifier",
436
+ name: "Humidifier",
437
+ device_class: :humidifier)
433
438
  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
439
  end
441
440
 
442
441
  # VSDrive can perform active dehumidification, even without a dedicated dehumidifier
@@ -446,28 +445,40 @@ class MQTTBridge
446
445
  "Dehumidifier Running",
447
446
  :boolean,
448
447
  @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 }
448
+ if @abc.awl_communicating?
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 }
463
+ 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)
463
470
  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
471
  end
472
+ next unless @abc.awl_communicating?
473
+
474
+ node.property("relative-humidity",
475
+ "Relative Humidity",
476
+ :integer,
477
+ @abc.humidistat.relative_humidity,
478
+ unit: "%",
479
+ format: 0..100,
480
+ hass: { sensor: { device_class: :humidity,
481
+ state_class: :measurement } })
471
482
  end
472
483
 
473
484
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
@@ -495,6 +506,14 @@ class MQTTBridge
495
506
  @abc.zones.each_with_index do |zone, i|
496
507
  type = zone.is_a?(Aurora::IZ2Zone) ? "IntelliZone 2 Zone" : "Thermostat"
497
508
  @homie.node("zone#{i + 1}", "Zone #{i + 1}", type) do |node|
509
+ node.property("current-mode",
510
+ "Current Heating/Cooling Mode Requested",
511
+ :enum,
512
+ zone.current_mode,
513
+ format: %w[standby h1 h2 h3 c1 c2])
514
+
515
+ next unless @abc.awl_communicating?
516
+
498
517
  allowed_modes = %w[off auto cool heat]
499
518
  allowed_modes << "eheat" if i.zero?
500
519
  node.property("target-mode",
@@ -504,11 +523,6 @@ class MQTTBridge
504
523
  format: allowed_modes) do |value, property|
505
524
  @mutex.synchronize { property.value = zone.target_mode = value.to_sym }
506
525
  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
526
  node.property("target-fan-mode",
513
527
  "Target Fan Mode",
514
528
  :enum,
@@ -86,6 +86,7 @@ module Aurora
86
86
  end
87
87
 
88
88
  attr_reader :modbus_slave,
89
+ :abc_version,
89
90
  :model,
90
91
  :serial_number,
91
92
  :zones,
@@ -111,14 +112,15 @@ module Aurora
111
112
  @modbus_slave = self.class.open_modbus_slave(uri)
112
113
  @modbus_slave.read_retry_timeout = 15
113
114
  @modbus_slave.read_retries = 2
114
- raw_registers = @modbus_slave.holding_registers[33, 88...110, 404, 412..413, 1103, 1114]
115
+ raw_registers = @modbus_slave.holding_registers[2, 33, 88...110, 404, 412..413, 813, 1103, 1114]
115
116
  registers = Aurora.transform_registers(raw_registers.dup)
117
+ @abc_version = registers[2]
116
118
  @program = registers[88]
117
119
  @model = registers[92]
118
120
  @serial_number = registers[105]
119
121
  @energy_monitor = raw_registers[412]
120
122
 
121
- @zones = if iz2?
123
+ @zones = if iz2? && iz2_version >= 2.0
122
124
  iz2_zone_count = @modbus_slave.holding_registers[483]
123
125
  (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
124
126
  else
@@ -152,8 +154,9 @@ module Aurora
152
154
 
153
155
  @faults = []
154
156
 
155
- @registers_to_read = [6, 19..20, 25, 30, 112, 344, 740..741, 900, 1104, 1110..1111, 1114, 1150..1153, 1165,
156
- 31_003]
157
+ @registers_to_read = [6, 19..20, 25, 30, 112, 344, 567, 1104, 1110..1111, 1114, 1150..1153, 1165]
158
+ @registers_to_read.concat([741, 31_003]) if awl_communicating?
159
+ @registers_to_read << 900 if awl_axb?
157
160
  zones.each do |z|
158
161
  @registers_to_read.concat(z.registers_to_read)
159
162
  end
@@ -178,8 +181,8 @@ module Aurora
178
181
 
179
182
  outputs = registers[30]
180
183
 
181
- @entering_air_temperature = registers[740]
182
- @leaving_air_temperature = registers[900]
184
+ @entering_air_temperature = registers[567]
185
+ @leaving_air_temperature = registers[900] if awl_axb?
183
186
  @leaving_water_temperature = registers[1110]
184
187
  @entering_water_temperature = registers[1111]
185
188
  @outdoor_temperature = registers[31_003]
@@ -270,13 +273,18 @@ module Aurora
270
273
  end
271
274
 
272
275
  # config aurora system
273
- { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
276
+ { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824, awl: 827 }.each do |(component, register)|
274
277
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
275
278
  def #{component}?
276
279
  return @#{component} if instance_variable_defined?(:@#{component})
277
280
  @#{component} = @modbus_slave.holding_registers[#{register}] != 3
278
281
  end
279
282
 
283
+ def #{component}_version
284
+ return @#{component}_version if instance_variable_defined?(:@#{component}_version)
285
+ @#{component}_version = @modbus_slave.holding_registers[#{register + 1}].to_f / 100
286
+ end
287
+
280
288
  def add_#{component}
281
289
  @modbus_slave.holding_registers[#{register}] = 2
282
290
  end
@@ -287,6 +295,27 @@ module Aurora
287
295
  RUBY
288
296
  end
289
297
 
298
+ # see https://www.waterfurnace.com/literature/symphony/ig2001ew.pdf
299
+ # is there a communicating system compliant with AWL?
300
+ def awl_communicating?
301
+ awl_thermostat? || awl_iz2?
302
+ end
303
+
304
+ # is the thermostat AWL compliant?
305
+ def awl_thermostat?
306
+ thermostat? && thermostat_version >= 3.0
307
+ end
308
+
309
+ # is the IZ2 AWL compliant?
310
+ def awl_iz2?
311
+ iz2? && iz2_version >= 2.0
312
+ end
313
+
314
+ # is the AXB AWL compliant?
315
+ def awl_axb?
316
+ axb? && axb_version >= 2.0
317
+ end
318
+
290
319
  def inspect
291
320
  "#<Aurora::ABCClient #{(instance_variables - [:@modbus_slave]).map do |iv|
292
321
  "#{iv}=#{instance_variable_get(iv).inspect}"
@@ -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.
@@ -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.7"
4
+ VERSION = "0.8.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waterfurnace_aurora
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer