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 +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
|