waterfurnace_aurora 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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