waterfurnace_aurora 0.5.1 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/aurora_mqtt_bridge +61 -9
- data/lib/aurora/abc_client.rb +33 -23
- data/lib/aurora/blower.rb +7 -14
- data/lib/aurora/compressor.rb +11 -6
- data/lib/aurora/dhw.rb +3 -1
- data/lib/aurora/humidistat.rb +93 -0
- data/lib/aurora/mqtt_modbus.rb +47 -0
- data/lib/aurora/pump.rb +3 -1
- data/lib/aurora/registers.rb +33 -30
- data/lib/aurora/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc02021d49b86596b6f1d40c244b3e3671280c578cc9cd869fc936f2c219a0bf
|
4
|
+
data.tar.gz: 795aa42ff626534a4750eb181e5299f82d0603f677a6b9d1fb2d8418046643c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 978a3a910d919a85a79f1d272ecec65b68908472492e25dcc8f7bc8f1ddc414f82ef1c22268e092ce026c0dbb90f48c73e9ede9f30144df3f2978fb5abf70b6b
|
7
|
+
data.tar.gz: 93336e684ab033ca48539738634e8835deffaac4fb5c7275269086e21a134f1f5ad2793c4ae859a806c385dec88811ed720ec837dda0c5f7cd4a068e24cb3338
|
data/exe/aurora_mqtt_bridge
CHANGED
@@ -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,
|
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,
|
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
|
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:
|
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
|
-
|
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
|
-
|
150
|
-
|
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
|
data/lib/aurora/abc_client.rb
CHANGED
@@ -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
|
-
@
|
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[
|
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
|
-
|
190
|
-
|
191
|
-
|
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
|
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
|
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?(:
|
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 <
|
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
|
|
data/lib/aurora/compressor.rb
CHANGED
@@ -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
|
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
|
43
|
-
|
47
|
+
def initialize(abc)
|
48
|
+
super(abc, 12)
|
44
49
|
end
|
45
50
|
|
46
|
-
def
|
47
|
-
|
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
|
|
data/lib/aurora/registers.rb
CHANGED
@@ -194,24 +194,24 @@ module Aurora
|
|
194
194
|
name ? "#{value} #{name}" : value.to_s
|
195
195
|
end
|
196
196
|
|
197
|
-
|
198
|
-
0 =>
|
199
|
-
1 =>
|
200
|
-
2 =>
|
201
|
-
3 =>
|
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 ?
|
209
|
-
fp2: value & 0x02 == 0x02 ?
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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[
|
306
|
-
result[
|
307
|
-
result[
|
308
|
-
result[
|
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[:
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
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",
|
901
|
-
31_110 => "Manual De/Humidification
|
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",
|
data/lib/aurora/version.rb
CHANGED
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.
|
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-
|
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
|