waterfurnace_aurora 0.5.1 → 0.6.1

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: edeaa2444792af1f963829496d445f87a29b5a67efad7e741937ab0f7278b222
4
- data.tar.gz: 37db08186651a5ee4486a9ee6dceb30f39ae4ca93122ae1c8e357b9b551c2929
3
+ metadata.gz: cc02021d49b86596b6f1d40c244b3e3671280c578cc9cd869fc936f2c219a0bf
4
+ data.tar.gz: 795aa42ff626534a4750eb181e5299f82d0603f677a6b9d1fb2d8418046643c6
5
5
  SHA512:
6
- metadata.gz: eed8903a3416f39f393ee1ec77d81a6c3c8c3db14ae3253e22ebf884e1eed624a7869dea9c76ef086a359c657c4f63d20fa94a801622846e6914943e38e0f827
7
- data.tar.gz: 5e326094aa14976dc8ba432944160c3073d47642bbdba272eca063ea00f6fdf0908f29a563b44f306b5af05754c6c77ae251c4cd225daaea9be8edc2c1101820
6
+ metadata.gz: 978a3a910d919a85a79f1d272ecec65b68908472492e25dcc8f7bc8f1ddc414f82ef1c22268e092ce026c0dbb90f48c73e9ede9f30144df3f2978fb5abf70b6b
7
+ data.tar.gz: 93336e684ab033ca48539738634e8835deffaac4fb5c7275269086e21a134f1f5ad2793c4ae859a806c385dec88811ed720ec837dda0c5f7cd4a068e24cb3338
@@ -46,8 +46,20 @@ class MQTTBridge
46
46
  when /\$modbus$/
47
47
  registers = @abc.query_registers(value)
48
48
  Aurora.print_registers(registers) do |register, v|
49
- @homie.mqtt.publish("#{@homie.topic}/$modbus/#{register}", v, retain: false, qos: 1)
49
+ @homie.mqtt.publish("#{@homie.topic}/$modbus/#{register}", v, qos: 1)
50
50
  end
51
+ when %r{\$modbus/getregs}
52
+ query_id, query = value.split(":")
53
+ queries = query.split(";").map do |range|
54
+ start, length = range.split(",").map(&:to_i)
55
+ next start if length.nil?
56
+
57
+ start...(start + length)
58
+ end
59
+ registers = @abc.modbus_slave.read_multiple_holding_registers(*queries)
60
+ result = registers.values.join(",")
61
+ @homie.mqtt.publish("#{@homie.topic}/$modbus/getregs/response",
62
+ "#{query_id}:#{result}", qos: 1)
51
63
  when %r{\$modbus/(\d+)/set$}
52
64
  register = $1.to_i
53
65
  value = case value
@@ -59,7 +71,7 @@ class MQTTBridge
59
71
  @abc.modbus_slave.holding_registers[register] = value if value
60
72
  registers = { register => @abc.modbus_slave.holding_registers[register] }
61
73
  Aurora.print_registers(registers) do |r, v|
62
- @homie.mqtt.publish("#{@homie.topic}/$modbus/#{r}", v, retain: false, qos: 1)
74
+ @homie.mqtt.publish("#{@homie.topic}/$modbus/#{r}", v, qos: 1)
63
75
  end
64
76
  end
65
77
  end
@@ -79,7 +91,8 @@ class MQTTBridge
79
91
  @compressor => @abc.compressor,
80
92
  @blower => @abc.blower,
81
93
  @pump => @abc.pump,
82
- @dhw => @abc.dhw }.compact
94
+ @dhw => @abc.dhw,
95
+ @humidistat => @abc.humidistat }.compact
83
96
  @abc.zones.each_with_index do |z, idx|
84
97
  homie_zone = @homie["zone#{idx + 1}"]
85
98
  components[homie_zone] = z
@@ -92,6 +105,8 @@ class MQTTBridge
92
105
  end
93
106
 
94
107
  @abc.faults.each_with_index do |fault_count, i|
108
+ next if fault_count == 0xffff
109
+
95
110
  @faults["e#{i + 1}"].value = fault_count
96
111
  end
97
112
  end
@@ -105,8 +120,10 @@ class MQTTBridge
105
120
 
106
121
  def publish_basic_attributes
107
122
  @homie_abc = @homie.node("abc", "Aurora Basic Control", "ABC") do |node|
123
+ allowed_modes = %w[lockout standby blower heating cooling eh1 eh2 emergency waiting]
124
+ allowed_modes << "dehumidify" if @abc.compressor.is_a?(Aurora::Compressor::VSDrive)
108
125
  node.property("current-mode", "Current Heating/Cooling Mode", :enum, @abc.current_mode,
109
- format: %w[lockout standby blower heating cooling eh1 eh2 emergency waiting dehumidify])
126
+ format: allowed_modes)
110
127
  node.property("entering-air-temperature", "Entering Air Temperature", :float, @abc.entering_air_temperature,
111
128
  unit: "ºF")
112
129
  node.property("entering-water-temperature", "Entering Water Temperature", :float,
@@ -118,8 +135,7 @@ class MQTTBridge
118
135
  unless @abc.outdoor_temperature.zero? # TODO: figure out the config if this actually exists
119
136
  node.property("outdoor-temperature", "Outdoor Temperature", :float, @abc.outdoor_temperature, unit: "ºF")
120
137
  end
121
- node.property("relative-humidity", "Relative Humidity", :integer, @abc.relative_humidity, unit: "%",
122
- format: 0..100)
138
+
123
139
  node.property("fp1", "FP1 Sensor", :float, @abc.fp1, unit: "ºF")
124
140
  node.property("fp2", "FP2 Sensor", :float, @abc.fp2, unit: "ºF")
125
141
  %i[aux_heat total].each do |component|
@@ -146,9 +162,8 @@ class MQTTBridge
146
162
  end
147
163
 
148
164
  @blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
149
- if @abc.blower.respond_to?(:running)
150
- node.property("running", "Blower is running", :boolean, @abc.blower.running?)
151
- else
165
+ node.property("running", "Blower is running", :boolean, @abc.blower.running?)
166
+ if @abc.blower.respond_to?(:speed)
152
167
  node.property("speed", "Current blower speed", :integer, @abc.blower.speed, format: @abc.blower.speed_range)
153
168
  end
154
169
  node.property("watts", "Energy Usage", :integer, @abc.blower.watts, unit: "W") if @abc.energy_monitoring?
@@ -177,6 +192,7 @@ class MQTTBridge
177
192
 
178
193
  next unless @abc.pump.is_a?(Aurora::Pump::VSPump)
179
194
 
195
+ node.property("running", "Pump is running", :boolean, @abc.pump.running)
180
196
  node.property("speed", "Speed", :integer, @abc.pump.speed, format: 0..100, unit: "%") do |value, property|
181
197
  @mutex.synchronize { property.value = @abc.pump.speed = value }
182
198
  end
@@ -199,6 +215,7 @@ class MQTTBridge
199
215
  node.property("enabled", "Enabled", :boolean, @abc.dhw.enabled) do |value, property|
200
216
  @mutex.synchronize { property.value = @abc.dhw.enabled = value }
201
217
  end
218
+ node.property("running", "DHW Pump is running", :boolean, @abc.dhw.running?)
202
219
  node.property("water-temperature", "Water Temperature", :float,
203
220
  @abc.dhw.water_temperature, unit: "ºF")
204
221
  node.property("set-point", "Set Point", :float, @abc.dhw.set_point, format: 100..140,
@@ -208,11 +225,45 @@ class MQTTBridge
208
225
  end
209
226
  end
210
227
 
228
+ @humidistat = @homie.node("humidistat", "Humidistat", "Humidistat") do |node|
229
+ node.property("relative-humidity", "Relative Humidity", :integer, @abc.humidistat.relative_humidity,
230
+ unit: "%", format: 0..100)
231
+ if @abc.humidistat.humidifier?
232
+ node.property("humidifier-running", "Humidifier is running", :boolean, @abc.humidistat.humidifier_running?)
233
+ node.property("humidifier-mode", "Humidifier Mode", :enum, @abc.humidistat.humidifier_mode,
234
+ format: %i[auto manual]) do |value, property|
235
+ @mutex.synchronize { property.value = @abc.humidistat.humidifier_mode = value.to_sym }
236
+ end
237
+ node.property("humidification-target", "Humidification target relative humidity", :integer,
238
+ @abc.humidistat.humidification_target, unit: "%", format: 15..50) do |value, property|
239
+ @mutex.synchronize { property.value = @abc.humidistat.humidification_target = value }
240
+ end
241
+ end
242
+
243
+ # VSDrive can perform active dehumidification, even without a dedicated dehumidifier
244
+ if @abc.humidistat.dehumidifier? || @abc.compressor.is_a?(Aurora::Compressor::VSDrive)
245
+ node.property("dehumidifier-mode", "Dehumidifier Mode", :enum, @abc.humidistat.dehumidifier_mode,
246
+ format: %i[auto manual]) do |value, property|
247
+ @mutex.synchronize { property.value = @abc.humidistat.dehumidifier_mode = value.to_sym }
248
+ end
249
+ node.property("dehumidification-target", "Dehumidification target relative humidity", :integer,
250
+ @abc.humidistat.dehumidification_target, unit: "%", format: 35..65) do |value, property|
251
+ @mutex.synchronize { property.value = @abc.humidistat.dehumidification_target = value }
252
+ end
253
+ end
254
+
255
+ next unless @abc.humidistat.dehumidifier?
256
+
257
+ node.property("dehumidifier-running", "Dehumidifier is running", :boolean, @abc.humidistat.dehumidifier_running?)
258
+ end
259
+
211
260
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
212
261
  node.property("clear-history", "Reset fault counts", :enum, retained: false, format: "clear") do |_|
213
262
  @mutex.synchronize { @abc.clear_fault_history }
214
263
  end
215
264
  @abc.faults.each_with_index do |count, i|
265
+ next if count == 0xffff
266
+
216
267
  name = Aurora::FAULTS[i + 1]
217
268
  node.property("e#{i + 1}", name || "E#{i + 1}", :integer, count)
218
269
  end
@@ -261,6 +312,7 @@ class MQTTBridge
261
312
 
262
313
  # direct access to modbus registers for debugging purposes
263
314
  @homie.mqtt.subscribe("#{@homie.topic}/$modbus")
315
+ @homie.mqtt.subscribe("#{@homie.topic}/$modbus/getregs")
264
316
  @homie.mqtt.subscribe("#{@homie.topic}/$modbus/+/set")
265
317
  @homie.publish
266
318
  end
@@ -6,6 +6,7 @@ require "uri"
6
6
  require "aurora/blower"
7
7
  require "aurora/compressor"
8
8
  require "aurora/dhw"
9
+ require "aurora/humidistat"
9
10
  require "aurora/iz2_zone"
10
11
  require "aurora/pump"
11
12
  require "aurora/thermostat"
@@ -26,7 +27,9 @@ module Aurora
26
27
  port: uri.port || 23,
27
28
  baud: 19_200,
28
29
  parity: :even)
29
-
30
+ when "mqtt", "mqtts"
31
+ require "aurora/mqtt_modbus"
32
+ return Aurora::MQTTModBus.new(uri)
30
33
  else
31
34
  return Aurora::MockABC.new(YAML.load_file(uri.path)) if File.file?(uri.path)
32
35
 
@@ -46,10 +49,10 @@ module Aurora
46
49
  :blower,
47
50
  :pump,
48
51
  :dhw,
52
+ :humidistat,
49
53
  :faults,
50
54
  :current_mode,
51
55
  :entering_air_temperature,
52
- :relative_humidity,
53
56
  :leaving_air_temperature,
54
57
  :leaving_water_temperature,
55
58
  :entering_water_temperature,
@@ -63,7 +66,7 @@ module Aurora
63
66
  @modbus_slave = self.class.open_modbus_slave(uri)
64
67
  @modbus_slave.read_retry_timeout = 15
65
68
  @modbus_slave.read_retries = 2
66
- raw_registers = @modbus_slave.holding_registers[88..91, 105...110, 404, 412..413, 1114]
69
+ raw_registers = @modbus_slave.holding_registers[33, 88..91, 105...110, 404, 412..413, 1103, 1114]
67
70
  registers = Aurora.transform_registers(raw_registers.dup)
68
71
  @program = registers[88]
69
72
  @serial_number = registers[105]
@@ -76,7 +79,14 @@ module Aurora
76
79
  [Thermostat.new(self)]
77
80
  end
78
81
 
79
- @compressor = @program == "ABCVSP" ? Compressor::VSDrive.new(self) : Compressor::GenericCompressor.new(self)
82
+ @abc_dipswitches = registers[33]
83
+ @axb_dipswitches = registers[1103]
84
+ @compressor = if @program == "ABCVSP"
85
+ Compressor::VSDrive.new(self)
86
+ else
87
+ Compressor::GenericCompressor.new(self,
88
+ @abc_dipswitches[:compressor])
89
+ end
80
90
  @blower = case raw_registers[404]
81
91
  when 1, 2 then Blower::ECM.new(self, registers[404])
82
92
  when 3 then Blower::FiveSpeed.new(self, registers[404])
@@ -90,8 +100,23 @@ module Aurora
90
100
  registers[413])
91
101
  end
92
102
  @dhw = DHW.new(self) if (-999..999).include?(registers[1114])
103
+ @humidistat = Humidistat.new(self,
104
+ @abc_dipswitches[:accessory_relay] == :humidifier,
105
+ @axb_dipswitches[:accessory_relay2] == :dehumidifier)
93
106
 
94
107
  @faults = []
108
+
109
+ @registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1104, 1110..1111, 1114, 1150..1153, 1165,
110
+ 31_003]
111
+ zones.each do |z|
112
+ @registers_to_read.concat(z.registers_to_read)
113
+ end
114
+ @components = [compressor, blower, pump, dhw, humidistat].compact
115
+ @components.each do |component|
116
+ @registers_to_read.concat(component.registers_to_read)
117
+ end
118
+ # need dehumidify mode to calculate final current mode
119
+ @registers_to_read.concat([362]) if compressor.is_a?(Compressor::VSDrive)
95
120
  end
96
121
 
97
122
  def query_registers(query)
@@ -129,29 +154,15 @@ module Aurora
129
154
  end
130
155
 
131
156
  def refresh
132
- registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114, 1150..1153, 1165,
133
- 31_003]
134
- zones.each do |z|
135
- registers_to_read.concat(z.registers_to_read)
136
- end
137
- registers_to_read.concat(compressor.registers_to_read)
138
- registers_to_read.concat(blower.registers_to_read)
139
- registers_to_read.concat(pump.registers_to_read)
140
- registers_to_read.concat(dhw.registers_to_read) if dhw
141
- # need dehumidify mode to calculate final current mode;
142
- # apparently non-VSD doesn't have this register at all?
143
- registers_to_read.concat([362]) if compressor.is_a?(Compressor::VSDrive)
144
-
145
157
  faults = @modbus_slave.read_multiple_holding_registers(601..699)
146
158
  @faults = Aurora.transform_registers(faults).values
147
159
 
148
- registers = @modbus_slave.holding_registers[*registers_to_read]
160
+ registers = @modbus_slave.holding_registers[*@registers_to_read]
149
161
  Aurora.transform_registers(registers)
150
162
 
151
163
  outputs = registers[30]
152
164
 
153
165
  @entering_air_temperature = registers[740]
154
- @relative_humidity = registers[741]
155
166
  @leaving_air_temperature = registers[900]
156
167
  @leaving_water_temperature = registers[1110]
157
168
  @entering_water_temperature = registers[1111]
@@ -186,10 +197,9 @@ module Aurora
186
197
  zones.each do |z|
187
198
  z.refresh(registers)
188
199
  end
189
- compressor.refresh(registers)
190
- blower.refresh(registers)
191
- pump.refresh(registers)
192
- dhw&.refresh(registers)
200
+ @components.each do |component|
201
+ component.refresh(registers)
202
+ end
193
203
  end
194
204
 
195
205
  def cooling_airflow_adjustment=(value)
data/lib/aurora/blower.rb CHANGED
@@ -4,8 +4,9 @@ require "aurora/component"
4
4
 
5
5
  module Aurora
6
6
  module Blower
7
- class GenericBlower < Component
8
- attr_reader :type, :watts
7
+ class PSC < Component
8
+ attr_reader :type, :watts, :running
9
+ alias running? running
9
10
 
10
11
  def initialize(abc, type)
11
12
  super(abc)
@@ -22,19 +23,11 @@ module Aurora
22
23
 
23
24
  def refresh(registers)
24
25
  @watts = registers[1148] if abc.energy_monitoring?
26
+ @running = registers[30].include?(:blower)
25
27
  end
26
28
  end
27
29
 
28
- class PSC < GenericBlower
29
- attr_reader :running
30
- alias running? running
31
-
32
- def refresh(registers)
33
- @running = registers[30].include?(:g)
34
- end
35
- end
36
-
37
- class FiveSpeed < GenericBlower
30
+ class FiveSpeed < PSC
38
31
  attr_reader :speed
39
32
 
40
33
  def speed_range
@@ -49,7 +42,7 @@ module Aurora
49
42
  3
50
43
  elsif outputs.include?(:cc)
51
44
  2
52
- elsif outputs.include?(:g)
45
+ elsif outputs.include?(:blower)
53
46
  1
54
47
  else
55
48
  0
@@ -57,7 +50,7 @@ module Aurora
57
50
  end
58
51
  end
59
52
 
60
- class ECM < GenericBlower
53
+ class ECM < PSC
61
54
  attr_reader :speed, :blower_only_speed, :low_compressor_speed, :high_compressor_speed, :aux_heat_speed,
62
55
  :iz2_desired_speed
63
56
 
@@ -7,12 +7,17 @@ module Aurora
7
7
  class GenericCompressor < Component
8
8
  attr_reader :speed, :watts
9
9
 
10
+ def initialize(abc, stages)
11
+ super(abc)
12
+ @stages = stages
13
+ end
14
+
10
15
  def type
11
- "Compressor"
16
+ "#{@stages == 2 ? 'Dual' : 'Single'} Stage Compressor"
12
17
  end
13
18
 
14
19
  def speed_range
15
- 0..2
20
+ 0..@stages
16
21
  end
17
22
 
18
23
  def registers_to_read
@@ -39,12 +44,12 @@ module Aurora
39
44
  class VSDrive < GenericCompressor
40
45
  attr_reader :ambient_temperature, :iz2_desired_speed
41
46
 
42
- def type
43
- "Variable Speed Drive"
47
+ def initialize(abc)
48
+ super(abc, 12)
44
49
  end
45
50
 
46
- def speed_range
47
- 0..12
51
+ def type
52
+ "Variable Speed Drive"
48
53
  end
49
54
 
50
55
  def registers_to_read
data/lib/aurora/dhw.rb CHANGED
@@ -4,7 +4,8 @@ require "aurora/component"
4
4
 
5
5
  module Aurora
6
6
  class DHW < Component
7
- attr_reader :enabled, :set_point, :water_temperature
7
+ attr_reader :enabled, :running, :set_point, :water_temperature
8
+ alias running? running
8
9
 
9
10
  def registers_to_read
10
11
  [400..401, 1114]
@@ -12,6 +13,7 @@ module Aurora
12
13
 
13
14
  def refresh(registers)
14
15
  @enabled = registers[400]
16
+ @running = registers[1104].include?(:dhw)
15
17
  @set_point = registers[401]
16
18
  @water_temperature = registers[1114]
17
19
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aurora/component"
4
+
5
+ module Aurora
6
+ class Humidistat < Component
7
+ attr_reader :humidifier_running, :humidifier_mode, :humidification_target,
8
+ :dehumidifier_running, :dehumidifier_mode, :dehumidification_target,
9
+ :relative_humidity
10
+ alias humidifier_running? humidifier_running
11
+ alias dehumidifer_running? dehumidifier_running
12
+
13
+ def initialize(abc, has_humidifier, has_dehumidifier)
14
+ super(abc)
15
+ @humidifier = has_humidifier
16
+ @dehumidifier = has_dehumidifier
17
+ end
18
+
19
+ def humidifier?
20
+ @humidifier
21
+ end
22
+
23
+ def dehumidifier?
24
+ @dehumidifier
25
+ end
26
+
27
+ def registers_to_read
28
+ result = [741]
29
+ if humidifier? || dehumidifier? || abc.compressor.is_a?(Compressor::VSDrive)
30
+ result.concat(abc.iz2? ? [21_114, 31_109..31_110] : [12_309..12_310])
31
+ end
32
+ result
33
+ end
34
+
35
+ def refresh(registers)
36
+ @relative_humidity = registers[741]
37
+
38
+ outputs = registers[30]
39
+ @humidifier_running = humidifier? && outputs.include?(:accessory)
40
+
41
+ outputs = registers[1104]
42
+ @dehumidifer_running = dehumidifier? && outputs.include?(:accessory2)
43
+
44
+ base = abc.iz2? ? 31_109 : 12_309
45
+ humidifier_settings_register = abc.iz2? ? 21_114 : 12_309
46
+ @humidifier_settings = registers[humidifier_settings_register]&.last&.[](2..)&.to_i(16)
47
+ @humidifier_mode = registers[humidifier_settings_register]&.include?(:auto_humidification) ? :auto : :manual
48
+ @dehumidifier_mode = registers[humidifier_settings_register]&.include?(:auto_dehumidification) ? :auto : :manual
49
+
50
+ @humidification_target = registers[base + 1]&.[](:humidification_target)
51
+ @dehumidification_target = registers[base + 1]&.[](:dehumidification_target)
52
+ end
53
+
54
+ def humidifier_mode=(mode)
55
+ set_humidistat_mode(mode, dehumidifier_mode)
56
+ end
57
+
58
+ def dehumidifier_mode=(mode)
59
+ set_humidistat_mode(humidifier_mode, mode)
60
+ end
61
+
62
+ def set_humidistat_mode(humidifier_mode, dehumidifier_mode)
63
+ allowed = %i[auto manual]
64
+ raise ArgumentError unless allowed.include?(humidifier_mode) && allowed.include?(dehumidifier_mode)
65
+
66
+ # start with the prior value of the register, since I'm not sure what
67
+ # else is stuffed in there
68
+ raw_value = @humidifier_settings
69
+ raw_value |= 0x4000 if humidifier_mode == :auto
70
+ raw_value |= 0x8000 if dehumidifier_mode == :auto
71
+ holding_registers[abc.iz2? ? 21_114 : 12_309] = raw_value
72
+ @humidifier_mode = humidifier_mode
73
+ @dehumidifier_mode = dehumidifier_mode
74
+ end
75
+
76
+ def humidification_target=(value)
77
+ set_humidistat_targets(value, dehumidification_target)
78
+ end
79
+
80
+ def dehumidification_target=(value)
81
+ set_humidistat_targets(humidification_target, value)
82
+ end
83
+
84
+ def set_humidistat_targets(humidification_target, dehumidification_target)
85
+ raise ArgumentError unless (15..50).include?(humidification_target)
86
+ raise ArgumentError unless (35..65).include?(dehumidification_target)
87
+
88
+ holding_registers[abc.iz2? ? 21_115 : 12_310] = (humidification_target << 8) + dehumidification_target
89
+ @humidification_target = humidification_target
90
+ @dehumidification_target = dehumidification_target
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mqtt"
4
+ require "securerandom"
5
+
6
+ module Aurora
7
+ class MQTTModBus
8
+ attr_accessor :logger
9
+
10
+ def initialize(uri)
11
+ @mqtt = MQTT::Client.new(uri)
12
+ @mqtt.connect
13
+
14
+ @base_topic = uri.path[1..]
15
+ @mqtt.subscribe("#{@base_topic}/getregs/response")
16
+ end
17
+
18
+ def read_multiple_holding_registers(*queries)
19
+ query_id = SecureRandom.uuid
20
+ mqtt_query = queries.map { |m| m.is_a?(Range) ? "#{m.begin},#{m.count}" : m }.join(";")
21
+ @mqtt.publish("#{@base_topic}/getregs", "#{query_id}:#{mqtt_query}", qos: 1)
22
+ Timeout.timeout(15) do
23
+ result = {}
24
+ loop do
25
+ packet = @mqtt.get
26
+ response_id, registers_flat = packet.payload.split(":")
27
+ next unless response_id == query_id
28
+
29
+ values = registers_flat.split(",").map(&:to_i)
30
+ result_index = 0
31
+ queries.each do |query|
32
+ Array(query).each do |i|
33
+ result[i] = values[result_index]
34
+ result_index += 1
35
+ end
36
+ end
37
+ break
38
+ end
39
+ result
40
+ end
41
+ end
42
+
43
+ def write_holding_register(addr, value)
44
+ @mqtt.publish("#{@base_topic}/#{addr}/set", value, qos: 1)
45
+ end
46
+ end
47
+ end
data/lib/aurora/pump.rb CHANGED
@@ -5,7 +5,8 @@ require "aurora/component"
5
5
  module Aurora
6
6
  module Pump
7
7
  class GenericPump < Component
8
- attr_reader :type, :watts, :waterflow
8
+ attr_reader :type, :watts, :waterflow, :running
9
+ alias running? running
9
10
 
10
11
  def initialize(abc, type)
11
12
  super(abc)
@@ -21,6 +22,7 @@ module Aurora
21
22
  def refresh(registers)
22
23
  @waterflow = registers[1117]
23
24
  @watts = registers[1164] if abc.energy_monitoring?
25
+ @running = registers[1104].include?(:loop_pump)
24
26
  end
25
27
  end
26
28
 
@@ -194,24 +194,24 @@ module Aurora
194
194
  name ? "#{value} #{name}" : value.to_s
195
195
  end
196
196
 
197
- AR_SETTINGS = {
198
- 0 => "Cycle with Compressor",
199
- 1 => "Slow Opening Water Valve",
200
- 2 => "Cycle with Thermostat Humidification Call",
201
- 3 => "Cycle with Blower"
197
+ ACCESSORY_RELAY_SETTINGS = {
198
+ 0 => :compressor,
199
+ 1 => :slow_opening_water_valve,
200
+ 2 => :humidifier,
201
+ 3 => :blower
202
202
  }.freeze
203
203
 
204
204
  def dipswitch_settings(value)
205
205
  return :manual if value == 0x7fff
206
206
 
207
207
  {
208
- fp1: value & 0x01 == 0x01 ? "30ºF" : "15ºF",
209
- fp2: value & 0x02 == 0x02 ? "30ºF" : "15ºF",
210
- rv: value & 0x04 == 0x04 ? "O" : "B",
211
- ar: AR_SETTINGS[(value >> 3) & 0x3],
212
- cc: value & 0x20 == 0x20 ? "Single Stage" : "Dual Stage",
213
- lo: value & 0x40 == 0x40 ? "Continouous" : "Pulse",
214
- dh_rh: value & 0x80 == 0x80 ? "Dehumidifier On" : "Reheat On"
208
+ fp1: value & 0x01 == 0x01 ? 30 : 15,
209
+ fp2: value & 0x02 == 0x02 ? 30 : :off,
210
+ reversing_valve: value & 0x04 == 0x04 ? :o : :b, # cycle to cool on O, or !B
211
+ accessory_relay: ACCESSORY_RELAY_SETTINGS[(value >> 3) & 0x3],
212
+ compressor: value & 0x20 == 0x20 ? 1 : 2, # single or dual stage compressor
213
+ lockout: value & 0x40 == 0x40 ? :continuous : :pulse,
214
+ dehumidifier_reheat: value & 0x80 == 0x80 ? :dehumidifier : :reheat
215
215
  }
216
216
  end
217
217
 
@@ -302,23 +302,23 @@ module Aurora
302
302
 
303
303
  def axb_inputs(value)
304
304
  result = {}
305
- result["Smart Grid"] = value & 0x001 == 0x001
306
- result["Home Automation 1"] = value & 0x002 == 0x002
307
- result["Home Automation 2"] = value & 0x004 == 0x004
308
- result["Pump Slave"] = value & 0x008 == 0x008
305
+ result[:smart_grid] = value & 0x001 == 0x001
306
+ result[:ha1] = value & 0x002 == 0x002
307
+ result[:ha2] = value & 0x004 == 0x004
308
+ result[:pump_slave] = value & 0x008 == 0x008
309
309
 
310
310
  result[:mb_address] = value & 0x010 == 0x010 ? 3 : 4
311
311
  result[:sw1_2] = value & 0x020 == 0x020 # future use # rubocop:disable Naming/VariableNumber
312
312
  result[:sw1_3] = value & 0x040 == 0x040 # future use # rubocop:disable Naming/VariableNumber
313
- result[:cycle_with] = if value & 0x080 == 0x080 && value & 0x100 == 0x100
314
- "Blower"
315
- elsif value & 0x100 == 0x100
316
- "Low Capacity Compressor"
317
- elsif value & 0x080 == 0x080
318
- "High Capacity Compressor"
319
- else
320
- "Thermostat Dehumidifier"
321
- end
313
+ result[:accessory_relay2] = if value & 0x080 == 0x080 && value & 0x100 == 0x100
314
+ :blower
315
+ elsif value & 0x100 == 0x100
316
+ :low_capacity_compressor
317
+ elsif value & 0x080 == 0x080
318
+ :high_capacity_compressor
319
+ else
320
+ :dehumidifier
321
+ end
322
322
  leftover = value & ~0x1ff
323
323
  result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
324
324
  result
@@ -523,7 +523,6 @@ module Aurora
523
523
  ->(v) { PUMP_TYPE[v] } => [413],
524
524
  ->(v) { PHASE_TYPE[v] } => [416],
525
525
  method(:iz2_fan_desired) => [565],
526
- ->(v) { v == 0xffff ? 0 : v } => 601..699,
527
526
  ->(registers, idx) { to_string(registers, idx, 8) } => [710],
528
527
  ->(v) { COMPONENT_STATUS[v] } => [800, 803, 806, 812, 815, 818, 824, 827],
529
528
  method(:axb_inputs) => [1103],
@@ -534,8 +533,8 @@ module Aurora
534
533
  method(:thermostat_configuration2) => [12_006],
535
534
  ->(v) { HEATING_MODE[v] } => [12_606, 21_202, 21_211, 21_220, 21_229, 21_238, 21_247],
536
535
  ->(v) { FAN_MODE[v] } => [12_621, 21_205, 21_214, 21_223, 21_232, 21_241, 21_250],
537
- ->(v) { from_bitmask(v, HUMIDIFIER_SETTINGS) } => [31_109],
538
- ->(v) { { humidification_target: v >> 8, dehumidification_target: v & 0xff } } => [31_110],
536
+ ->(v) { from_bitmask(v, HUMIDIFIER_SETTINGS) } => [12_309, 21_114, 31_109],
537
+ ->(v) { { humidification_target: v >> 8, dehumidification_target: v & 0xff } } => [12_310, 21_115, 31_110],
539
538
  method(:iz2_demand) => [31_005],
540
539
  method(:zone_configuration1) => [12_005, 31_008, 31_011, 31_014, 31_017, 31_020, 31_023],
541
540
  method(:zone_configuration2) => [31_009, 31_012, 31_015, 31_018, 31_021, 31_024],
@@ -889,16 +888,20 @@ module Aurora
889
888
  3906 => "VS Drive SuperHeat Temperature",
890
889
  12_005 => "Fan Configuration",
891
890
  12_006 => "Heating Mode",
891
+ 12_309 => "De/Humidifier Mode",
892
+ 12_310 => "De/Humidifier Setpoints",
892
893
  12_606 => "Heating Mode (write)",
893
894
  12_619 => "Heating Setpoint (write)",
894
895
  12_620 => "Cooling Setpoint (write)",
895
896
  12_621 => "Fan Mode (write)",
896
897
  12_622 => "Intermittent Fan On Time (write)",
897
898
  12_623 => "Intermittent Fan Off Time (write)",
899
+ 21_114 => "IZ2 De/Humidifier Mode (write)",
900
+ 21_115 => "IZ2 De/Humidifier Setpoints (write)",
898
901
  31_003 => "Outdoor Temp",
899
902
  31_005 => "IZ2 Demand",
900
- 31_109 => "Humidifier Mode", # write to 21114
901
- 31_110 => "Manual De/Humidification Target", # write to 21115
903
+ 31_109 => "De/Humidifier Mode",
904
+ 31_110 => "Manual De/Humidification Setpoints",
902
905
  31_400 => "Dealer Name",
903
906
  31_413 => "Dealer Phone",
904
907
  31_421 => "Dealer Address 1",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aurora
4
- VERSION = "0.5.1"
4
+ VERSION = "0.6.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.5.1
4
+ version: 0.6.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: 2021-09-01 00:00:00.000000000 Z
11
+ date: 2021-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ccutrer-serialport
@@ -139,10 +139,12 @@ files:
139
139
  - lib/aurora/compressor.rb
140
140
  - lib/aurora/core_ext/string.rb
141
141
  - lib/aurora/dhw.rb
142
+ - lib/aurora/humidistat.rb
142
143
  - lib/aurora/iz2_zone.rb
143
144
  - lib/aurora/mock_abc.rb
144
145
  - lib/aurora/modbus/server.rb
145
146
  - lib/aurora/modbus/slave.rb
147
+ - lib/aurora/mqtt_modbus.rb
146
148
  - lib/aurora/pump.rb
147
149
  - lib/aurora/registers.rb
148
150
  - lib/aurora/thermostat.rb