waterfurnace_aurora 0.4.5 → 0.5.3
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 +68 -38
- data/lib/aurora/abc_client.rb +42 -66
- data/lib/aurora/component.rb +6 -0
- data/lib/aurora/compressor.rb +70 -0
- data/lib/aurora/dhw.rb +31 -0
- data/lib/aurora/humidifier.rb +15 -0
- data/lib/aurora/iz2_zone.rb +9 -5
- data/lib/aurora/mqtt_modbus.rb +47 -0
- data/lib/aurora/registers.rb +30 -15
- data/lib/aurora/thermostat.rb +37 -5
- data/lib/aurora/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a372b51d3240840ae96269020f73728fb049f3e6c0438613c9659d4158d21aa6
|
4
|
+
data.tar.gz: 249e26d5764a0ab670a60b5de7ed6da0796344fd71896c6c33d353fc4a7d95ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5c5ef5a5e1d6ec3d393913ca1b717a5a53a5f17c51f2c9b8ea0f6aeb627cdc873fa25e22511a779d7b9c0e9a60fdc21bc63dbb1fb0d8f6baa41c3dada85cb91
|
7
|
+
data.tar.gz: ee787175c344db12fe11f0dab4131505e5692ca360adeee78bcd0931bd4b28660de9c0d09817c5f30632808b374a3695f8a8cbf9f1e965bf410648f411ffa958
|
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
|
@@ -76,8 +88,11 @@ class MQTTBridge
|
|
76
88
|
@abc.refresh
|
77
89
|
|
78
90
|
components = { @homie_abc => @abc,
|
91
|
+
@compressor => @abc.compressor,
|
79
92
|
@blower => @abc.blower,
|
80
|
-
@pump => @abc.pump
|
93
|
+
@pump => @abc.pump,
|
94
|
+
@dhw => @abc.dhw,
|
95
|
+
@humidifier => @abc.humidifier }.compact
|
81
96
|
@abc.zones.each_with_index do |z, idx|
|
82
97
|
homie_zone = @homie["zone#{idx + 1}"]
|
83
98
|
components[homie_zone] = z
|
@@ -89,12 +104,6 @@ class MQTTBridge
|
|
89
104
|
end
|
90
105
|
end
|
91
106
|
|
92
|
-
if @abc.dhw?
|
93
|
-
@dhw["enabled"].value = @abc.dhw_enabled
|
94
|
-
@dhw["water-temperature"].value = @abc.dhw_water_temperature
|
95
|
-
@dhw["set-point"].value = @abc.dhw_setpoint
|
96
|
-
end
|
97
|
-
|
98
107
|
@abc.faults.each_with_index do |fault_count, i|
|
99
108
|
@faults["e#{i + 1}"].value = fault_count
|
100
109
|
end
|
@@ -109,8 +118,6 @@ class MQTTBridge
|
|
109
118
|
|
110
119
|
def publish_basic_attributes
|
111
120
|
@homie_abc = @homie.node("abc", "Aurora Basic Control", "ABC") do |node|
|
112
|
-
node.property("compressor-speed", "Compressor Speed", :integer, @abc.compressor_speed,
|
113
|
-
format: @abc.vs_drive? ? 0..12 : 0..2)
|
114
121
|
node.property("current-mode", "Current Heating/Cooling Mode", :enum, @abc.current_mode,
|
115
122
|
format: %w[lockout standby blower heating cooling eh1 eh2 emergency waiting dehumidify])
|
116
123
|
node.property("entering-air-temperature", "Entering Air Temperature", :float, @abc.entering_air_temperature,
|
@@ -128,13 +135,29 @@ class MQTTBridge
|
|
128
135
|
format: 0..100)
|
129
136
|
node.property("fp1", "FP1 Sensor", :float, @abc.fp1, unit: "ºF")
|
130
137
|
node.property("fp2", "FP2 Sensor", :float, @abc.fp2, unit: "ºF")
|
131
|
-
%i[
|
138
|
+
%i[aux_heat total].each do |component|
|
132
139
|
component = "#{component}_watts"
|
133
140
|
node.property(component.tr("_", "-"), component.tr("_", " ").titleize, :integer,
|
134
141
|
@abc.public_send(component), unit: "W")
|
135
142
|
end
|
136
143
|
end
|
137
144
|
|
145
|
+
@compressor = @homie.node("compressor", "Compressor", @abc.compressor.type) do |node|
|
146
|
+
node.property("speed", "Current compressor speed", :integer, @abc.compressor.speed,
|
147
|
+
format: @abc.compressor.speed_range)
|
148
|
+
node.property("watts", "Energy Usage", :integer, @abc.compressor.watts, unit: "W") if @abc.energy_monitoring?
|
149
|
+
|
150
|
+
next unless @abc.compressor.is_a?(Aurora::Compressor::VSDrive)
|
151
|
+
|
152
|
+
node.property("ambient-temperature", "Compressor ambient temperature", :float,
|
153
|
+
@abc.compressor.ambient_temperature, unit: "ºF")
|
154
|
+
|
155
|
+
next unless @abc.iz2?
|
156
|
+
|
157
|
+
node.property("iz2-desired-speed", "IZ2 Desired Speed", :integer, @abc.compressor.iz2_desired_speed,
|
158
|
+
format: 0..12)
|
159
|
+
end
|
160
|
+
|
138
161
|
@blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
|
139
162
|
if @abc.blower.respond_to?(:running)
|
140
163
|
node.property("running", "Blower is running", :boolean, @abc.blower.running?)
|
@@ -154,10 +177,11 @@ class MQTTBridge
|
|
154
177
|
@mutex.synchronize { property.value = @abc.blower.public_send("#{field}=", value) }
|
155
178
|
end
|
156
179
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
180
|
+
|
181
|
+
next unless @abc.iz2?
|
182
|
+
|
183
|
+
node.property("iz2-desired-speed", "IZ2 Desired Speed", :integer, @abc.blower.iz2_desired_speed,
|
184
|
+
format: 0..100, unit: "%")
|
161
185
|
end
|
162
186
|
|
163
187
|
@pump = @homie.node("pump", "Loop Pump", @abc.pump.type) do |node|
|
@@ -183,21 +207,30 @@ class MQTTBridge
|
|
183
207
|
end
|
184
208
|
end
|
185
209
|
|
186
|
-
if @abc.dhw
|
210
|
+
if @abc.dhw
|
187
211
|
@dhw = @homie.node("dhw", "Domestic Hot Water Generator", "DHW") do |node|
|
188
|
-
node.property("enabled", "Enabled", :boolean, @abc.
|
189
|
-
@mutex.synchronize { property.value = @abc.
|
212
|
+
node.property("enabled", "Enabled", :boolean, @abc.dhw.enabled) do |value, property|
|
213
|
+
@mutex.synchronize { property.value = @abc.dhw.enabled = value }
|
190
214
|
end
|
191
|
-
node.property("water-temperature", "
|
192
|
-
@abc.
|
193
|
-
node.property("set-point", "
|
194
|
-
|
195
|
-
@mutex.synchronize { property.value = @abc.
|
215
|
+
node.property("water-temperature", "Water Temperature", :float,
|
216
|
+
@abc.dhw.water_temperature, unit: "ºF")
|
217
|
+
node.property("set-point", "Set Point", :float, @abc.dhw.set_point, format: 100..140,
|
218
|
+
unit: "ºF") do |value, property|
|
219
|
+
@mutex.synchronize { property.value = @abc.dhw.set_point = value }
|
196
220
|
end
|
197
221
|
end
|
198
222
|
end
|
199
223
|
|
224
|
+
if @abc.humidifier
|
225
|
+
@humidifier = @homie.node("humidifier", "Humidifier", "Humidifier") do |node|
|
226
|
+
node.property("running", "Humidifier is running", :boolean, @abc.humidifier.running?)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
200
230
|
@faults = @homie.node("faults", "Fault History", "ABC") do |node|
|
231
|
+
node.property("clear-history", "Reset fault counts", :enum, retained: false, format: "clear") do |_|
|
232
|
+
@mutex.synchronize { @abc.clear_fault_history }
|
233
|
+
end
|
201
234
|
@abc.faults.each_with_index do |count, i|
|
202
235
|
name = Aurora::FAULTS[i + 1]
|
203
236
|
node.property("e#{i + 1}", name || "E#{i + 1}", :integer, count)
|
@@ -213,25 +246,21 @@ class MQTTBridge
|
|
213
246
|
format: allowed_modes) do |value, property|
|
214
247
|
@mutex.synchronize { property.value = zone.target_mode = value.to_sym }
|
215
248
|
end
|
216
|
-
|
217
|
-
|
218
|
-
format: %w[standby h1 h2 h3 c1 c2])
|
219
|
-
end
|
249
|
+
node.property("current-mode", "Current Heating/Cooling Mode Requested", :enum, zone.current_mode,
|
250
|
+
format: %w[standby h1 h2 h3 c1 c2])
|
220
251
|
node.property("target-fan-mode", "Target Fan Mode", :enum, zone.target_fan_mode,
|
221
252
|
format: %w[auto continuous intermittent]) do |value, property|
|
222
253
|
@mutex.synchronize { property.value = zone.target_fan_mode = value.to_sym }
|
223
254
|
end
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
unit: "M", format: %w[0 5 10 15 20 25 30 35 40]) do |value, property|
|
232
|
-
@mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
|
233
|
-
end
|
255
|
+
node.property("fan-intermittent-on", "Fan Intermittent Mode On Duration", :enum, zone.fan_intermittent_on,
|
256
|
+
unit: "M", format: %w[0 5 10 15 20]) do |value, property|
|
257
|
+
@mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
|
258
|
+
end
|
259
|
+
node.property("fan-intermittent-off", "Fan Intermittent Mode Off Duration", :enum, zone.fan_intermittent_on,
|
260
|
+
unit: "M", format: %w[0 5 10 15 20 25 30 35 40]) do |value, property|
|
261
|
+
@mutex.synchronize { property.value = zone.fan_intermittent_on = value.to_i }
|
234
262
|
end
|
263
|
+
node.property("current-fan-mode", "Current Fan Status", :boolean, zone.current_fan_mode)
|
235
264
|
if zone.is_a?(Aurora::IZ2Zone)
|
236
265
|
node.property("priority", "Zone Priority", :enum, zone.priority, format: %w[economy comfort])
|
237
266
|
node.property("size", "Size", :enum, zone.size, format: %w[0 25 45 70])
|
@@ -251,6 +280,7 @@ class MQTTBridge
|
|
251
280
|
|
252
281
|
# direct access to modbus registers for debugging purposes
|
253
282
|
@homie.mqtt.subscribe("#{@homie.topic}/$modbus")
|
283
|
+
@homie.mqtt.subscribe("#{@homie.topic}/$modbus/getregs")
|
254
284
|
@homie.mqtt.subscribe("#{@homie.topic}/$modbus/+/set")
|
255
285
|
@homie.publish
|
256
286
|
end
|
data/lib/aurora/abc_client.rb
CHANGED
@@ -4,6 +4,9 @@ require "yaml"
|
|
4
4
|
require "uri"
|
5
5
|
|
6
6
|
require "aurora/blower"
|
7
|
+
require "aurora/compressor"
|
8
|
+
require "aurora/dhw"
|
9
|
+
require "aurora/humidifier"
|
7
10
|
require "aurora/iz2_zone"
|
8
11
|
require "aurora/pump"
|
9
12
|
require "aurora/thermostat"
|
@@ -24,7 +27,9 @@ module Aurora
|
|
24
27
|
port: uri.port || 23,
|
25
28
|
baud: 19_200,
|
26
29
|
parity: :even)
|
27
|
-
|
30
|
+
when "mqtt", "mqtts"
|
31
|
+
require "aurora/mqtt_modbus"
|
32
|
+
return Aurora::MQTTModBus.new(uri)
|
28
33
|
else
|
29
34
|
return Aurora::MockABC.new(YAML.load_file(uri.path)) if File.file?(uri.path)
|
30
35
|
|
@@ -40,23 +45,21 @@ module Aurora
|
|
40
45
|
attr_reader :modbus_slave,
|
41
46
|
:serial_number,
|
42
47
|
:zones,
|
48
|
+
:compressor,
|
43
49
|
:blower,
|
44
50
|
:pump,
|
51
|
+
:dhw,
|
52
|
+
:humidifier,
|
45
53
|
:faults,
|
46
54
|
:current_mode,
|
47
|
-
:dhw_enabled,
|
48
|
-
:dhw_setpoint,
|
49
55
|
:entering_air_temperature,
|
50
56
|
:relative_humidity,
|
51
57
|
:leaving_air_temperature,
|
52
58
|
:leaving_water_temperature,
|
53
59
|
:entering_water_temperature,
|
54
|
-
:dhw_water_temperature,
|
55
|
-
:compressor_speed,
|
56
60
|
:outdoor_temperature,
|
57
61
|
:fp1,
|
58
62
|
:fp2,
|
59
|
-
:compressor_watts,
|
60
63
|
:aux_heat_watts,
|
61
64
|
:total_watts
|
62
65
|
|
@@ -64,13 +67,26 @@ module Aurora
|
|
64
67
|
@modbus_slave = self.class.open_modbus_slave(uri)
|
65
68
|
@modbus_slave.read_retry_timeout = 15
|
66
69
|
@modbus_slave.read_retries = 2
|
67
|
-
raw_registers = @modbus_slave.holding_registers[88..91, 105...110, 404, 412..413, 1114]
|
70
|
+
raw_registers = @modbus_slave.holding_registers[33, 88..91, 105...110, 404, 412..413, 1114]
|
68
71
|
registers = Aurora.transform_registers(raw_registers.dup)
|
69
72
|
@program = registers[88]
|
70
73
|
@serial_number = registers[105]
|
71
|
-
@dhw_water_temperature = registers[1114]
|
72
74
|
@energy_monitor = raw_registers[412]
|
73
75
|
|
76
|
+
@zones = if iz2?
|
77
|
+
iz2_zone_count = @modbus_slave.holding_registers[483]
|
78
|
+
(0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
|
79
|
+
else
|
80
|
+
[Thermostat.new(self)]
|
81
|
+
end
|
82
|
+
|
83
|
+
@abc_dipswitches = registers[33]
|
84
|
+
@compressor = if @program == "ABCVSP"
|
85
|
+
Compressor::VSDrive.new(self)
|
86
|
+
else
|
87
|
+
Compressor::GenericCompressor.new(self,
|
88
|
+
@abc_dipswitches[:compressor])
|
89
|
+
end
|
74
90
|
@blower = case raw_registers[404]
|
75
91
|
when 1, 2 then Blower::ECM.new(self, registers[404])
|
76
92
|
when 3 then Blower::FiveSpeed.new(self, registers[404])
|
@@ -83,13 +99,9 @@ module Aurora
|
|
83
99
|
Pump::GenericPump.new(self,
|
84
100
|
registers[413])
|
85
101
|
end
|
102
|
+
@dhw = DHW.new(self) if (-999..999).include?(registers[1114])
|
103
|
+
@humidifier = Humidifier.new(self) if @abc_dipswitches[:accessory_relay] == :humidifier
|
86
104
|
|
87
|
-
@zones = if iz2?
|
88
|
-
iz2_zone_count = @modbus_slave.holding_registers[483]
|
89
|
-
(0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
|
90
|
-
else
|
91
|
-
[Thermostat.new(self)]
|
92
|
-
end
|
93
105
|
@faults = []
|
94
106
|
end
|
95
107
|
|
@@ -128,51 +140,32 @@ module Aurora
|
|
128
140
|
end
|
129
141
|
|
130
142
|
def refresh
|
131
|
-
registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114,
|
143
|
+
registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114, 1150..1153, 1165,
|
132
144
|
31_003]
|
133
|
-
|
145
|
+
zones.each do |z|
|
146
|
+
registers_to_read.concat(z.registers_to_read)
|
147
|
+
end
|
148
|
+
registers_to_read.concat(compressor.registers_to_read)
|
134
149
|
registers_to_read.concat(blower.registers_to_read)
|
135
150
|
registers_to_read.concat(pump.registers_to_read)
|
136
|
-
registers_to_read.concat(
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
base1 = 21_203 + i * 9
|
141
|
-
base2 = 31_007 + i * 3
|
142
|
-
base3 = 31_200 + i * 3
|
143
|
-
registers_to_read << (base1..(base1 + 1))
|
144
|
-
registers_to_read << (base2..(base2 + 2))
|
145
|
-
registers_to_read << base3
|
146
|
-
end
|
147
|
-
else
|
148
|
-
registers_to_read << 502
|
149
|
-
registers_to_read << (745..746)
|
150
|
-
end
|
151
|
+
registers_to_read.concat(dhw.registers_to_read) if dhw
|
152
|
+
# need dehumidify mode to calculate final current mode;
|
153
|
+
# apparently non-VSD doesn't have this register at all?
|
154
|
+
registers_to_read.concat([362]) if compressor.is_a?(Compressor::VSDrive)
|
151
155
|
|
152
|
-
|
156
|
+
faults = @modbus_slave.read_multiple_holding_registers(601..699)
|
157
|
+
@faults = Aurora.transform_registers(faults).values
|
153
158
|
|
154
159
|
registers = @modbus_slave.holding_registers[*registers_to_read]
|
155
160
|
Aurora.transform_registers(registers)
|
156
161
|
|
157
162
|
outputs = registers[30]
|
158
163
|
|
159
|
-
@dhw_enabled = registers[400]
|
160
|
-
@dhw_setpoint = registers[401]
|
161
164
|
@entering_air_temperature = registers[740]
|
162
165
|
@relative_humidity = registers[741]
|
163
166
|
@leaving_air_temperature = registers[900]
|
164
167
|
@leaving_water_temperature = registers[1110]
|
165
168
|
@entering_water_temperature = registers[1111]
|
166
|
-
@dhw_water_temperature = registers[1114]
|
167
|
-
@compressor_speed = if vs_drive?
|
168
|
-
registers[3001]
|
169
|
-
elsif outputs.include?(:cc2)
|
170
|
-
2
|
171
|
-
elsif outputs.include?(:cc)
|
172
|
-
1
|
173
|
-
else
|
174
|
-
0
|
175
|
-
end
|
176
169
|
@outdoor_temperature = registers[31_003]
|
177
170
|
@fp1 = registers[19]
|
178
171
|
@fp2 = registers[20]
|
@@ -180,7 +173,6 @@ module Aurora
|
|
180
173
|
@error = registers[25] & 0x7fff
|
181
174
|
@derated = (41..46).include?(@error)
|
182
175
|
@safe_mode = [47, 48, 49, 72, 74].include?(@error)
|
183
|
-
@compressor_watts = registers[1147]
|
184
176
|
@aux_heat_watts = registers[1151]
|
185
177
|
@total_watts = registers[1153]
|
186
178
|
|
@@ -202,12 +194,14 @@ module Aurora
|
|
202
194
|
:standby
|
203
195
|
end
|
204
196
|
|
205
|
-
blower.refresh(registers)
|
206
|
-
pump.refresh(registers)
|
207
|
-
|
208
197
|
zones.each do |z|
|
209
198
|
z.refresh(registers)
|
210
199
|
end
|
200
|
+
compressor.refresh(registers)
|
201
|
+
blower.refresh(registers)
|
202
|
+
pump.refresh(registers)
|
203
|
+
dhw&.refresh(registers)
|
204
|
+
humidifier&.refresh(registers)
|
211
205
|
end
|
212
206
|
|
213
207
|
def cooling_airflow_adjustment=(value)
|
@@ -215,16 +209,6 @@ module Aurora
|
|
215
209
|
@modbus_slave.holding_registers[346] = value
|
216
210
|
end
|
217
211
|
|
218
|
-
def dhw_enabled=(value)
|
219
|
-
@modbus_slave.holding_registers[400] = value ? 1 : 0
|
220
|
-
end
|
221
|
-
|
222
|
-
def dhw_setpoint=(value)
|
223
|
-
raise ArgumentError unless (100..140).include?(value)
|
224
|
-
|
225
|
-
@modbus_slave.holding_registers[401] = (value * 10).to_i
|
226
|
-
end
|
227
|
-
|
228
212
|
def loop_pressure_trip=(value)
|
229
213
|
@modbus_slave.holding_registers[419] = (value * 10).to_i
|
230
214
|
end
|
@@ -270,14 +254,6 @@ module Aurora
|
|
270
254
|
@energy_monitor == 2
|
271
255
|
end
|
272
256
|
|
273
|
-
def vs_drive?
|
274
|
-
@program == "ABCVSP"
|
275
|
-
end
|
276
|
-
|
277
|
-
def dhw?
|
278
|
-
(-999..999).include?(dhw_water_temperature)
|
279
|
-
end
|
280
|
-
|
281
257
|
# config aurora system
|
282
258
|
{ thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
|
283
259
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
data/lib/aurora/component.rb
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aurora/component"
|
4
|
+
|
5
|
+
module Aurora
|
6
|
+
module Compressor
|
7
|
+
class GenericCompressor < Component
|
8
|
+
attr_reader :speed, :watts
|
9
|
+
|
10
|
+
def initialize(abc, stages)
|
11
|
+
super(abc)
|
12
|
+
@stages = stages
|
13
|
+
end
|
14
|
+
|
15
|
+
def type
|
16
|
+
"#{@stages == 2 ? 'Dual' : 'Single'} Stage Compressor"
|
17
|
+
end
|
18
|
+
|
19
|
+
def speed_range
|
20
|
+
0..@stages
|
21
|
+
end
|
22
|
+
|
23
|
+
def registers_to_read
|
24
|
+
if abc.energy_monitoring?
|
25
|
+
[1146..1147]
|
26
|
+
else
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def refresh(registers)
|
32
|
+
outputs = registers[30]
|
33
|
+
@speed = if outputs.include?(:cc2)
|
34
|
+
2
|
35
|
+
elsif outputs.include?(:cc)
|
36
|
+
1
|
37
|
+
else
|
38
|
+
0
|
39
|
+
end
|
40
|
+
@watts = registers[1146] if abc.energy_monitoring?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class VSDrive < GenericCompressor
|
45
|
+
attr_reader :ambient_temperature, :iz2_desired_speed
|
46
|
+
|
47
|
+
def initialize(abc)
|
48
|
+
super(abc, 12)
|
49
|
+
end
|
50
|
+
|
51
|
+
def type
|
52
|
+
"Variable Speed Drive"
|
53
|
+
end
|
54
|
+
|
55
|
+
def registers_to_read
|
56
|
+
result = super + [209, 3001, 3326]
|
57
|
+
result << 564 if abc.iz2?
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def refresh(registers)
|
62
|
+
super
|
63
|
+
|
64
|
+
@speed = registers[3001]
|
65
|
+
@ambient_temperature = registers[3326]
|
66
|
+
@iz2_desired_speed = registers[564] if abc.iz2?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/aurora/dhw.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aurora/component"
|
4
|
+
|
5
|
+
module Aurora
|
6
|
+
class DHW < Component
|
7
|
+
attr_reader :enabled, :set_point, :water_temperature
|
8
|
+
|
9
|
+
def registers_to_read
|
10
|
+
[400..401, 1114]
|
11
|
+
end
|
12
|
+
|
13
|
+
def refresh(registers)
|
14
|
+
@enabled = registers[400]
|
15
|
+
@set_point = registers[401]
|
16
|
+
@water_temperature = registers[1114]
|
17
|
+
end
|
18
|
+
|
19
|
+
def enabled=(value)
|
20
|
+
holding_registers[400] = value ? 1 : 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_point=(value) # rubocop:disable Naming/AccessorMethodName
|
24
|
+
raise ArgumentError unless (100..140).include?(value)
|
25
|
+
|
26
|
+
raw_value = (value * 10).to_i
|
27
|
+
holding_registers[401] = raw_value
|
28
|
+
@set_point = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aurora/component"
|
4
|
+
|
5
|
+
module Aurora
|
6
|
+
class Humidifier < Component
|
7
|
+
attr_reader :running
|
8
|
+
alias running? running
|
9
|
+
|
10
|
+
def refresh(registers)
|
11
|
+
outputs = registers[30]
|
12
|
+
@running = outputs.include?(:accessory)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/aurora/iz2_zone.rb
CHANGED
@@ -5,18 +5,22 @@ require "aurora/thermostat"
|
|
5
5
|
module Aurora
|
6
6
|
class IZ2Zone < Thermostat
|
7
7
|
attr_reader :zone_number,
|
8
|
-
:current_mode,
|
9
|
-
:current_fan_mode,
|
10
|
-
:fan_intermittent_on,
|
11
|
-
:fan_intermittent_off,
|
12
8
|
:priority,
|
13
|
-
:size,
|
9
|
+
:size,
|
10
|
+
:normalized_size
|
14
11
|
|
15
12
|
def initialize(abc, zone_number)
|
16
13
|
super(abc)
|
17
14
|
@zone_number = zone_number
|
18
15
|
end
|
19
16
|
|
17
|
+
def registers_to_read
|
18
|
+
base1 = 21_203 + (zone_number - 1) * 9
|
19
|
+
base2 = 31_007 + (zone_number - 1) * 3
|
20
|
+
base3 = 31_200 + (zone_number - 1) * 3
|
21
|
+
[base1..(base1 + 1), base2..(base2 + 2), base3]
|
22
|
+
end
|
23
|
+
|
20
24
|
def refresh(registers)
|
21
25
|
@ambient_temperature = registers[31_007 + (zone_number - 1) * 3]
|
22
26
|
|
@@ -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/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
|
|
@@ -420,6 +420,15 @@ module Aurora
|
|
420
420
|
end
|
421
421
|
end
|
422
422
|
|
423
|
+
def thermostat_configuration2(value)
|
424
|
+
result = {
|
425
|
+
mode: HEATING_MODE[(value >> 8) & 0x07]
|
426
|
+
}
|
427
|
+
leftover = value & ~0x0700
|
428
|
+
result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
|
429
|
+
result
|
430
|
+
end
|
431
|
+
|
423
432
|
def zone_configuration1(value)
|
424
433
|
fan = if value & 0x80 == 0x80
|
425
434
|
:continuous
|
@@ -433,7 +442,7 @@ module Aurora
|
|
433
442
|
on_time: ((value >> 9) & 0x7) * 5,
|
434
443
|
off_time: (((value >> 12) & 0x7) + 1) * 5,
|
435
444
|
cooling_target_temperature: ((value & 0x7e) >> 1) + 36,
|
436
|
-
heating_target_temperature_carry: value &
|
445
|
+
heating_target_temperature_carry: value & 0x01
|
437
446
|
}
|
438
447
|
leftover = value & ~0x7fff
|
439
448
|
result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
|
@@ -503,7 +512,7 @@ module Aurora
|
|
503
512
|
->(v) { from_bitmask(v, VS_ALARM2) } => [218, 3227],
|
504
513
|
->(v) { from_bitmask(v, VS_EEV2) } => [280, 3804],
|
505
514
|
method(:vs_manual_control) => [323],
|
506
|
-
NEGATABLE => [346
|
515
|
+
NEGATABLE => [346],
|
507
516
|
->(v) { BRINE_TYPE[v] } => [402],
|
508
517
|
->(v) { FLOW_METER_TYPE[v] } => [403],
|
509
518
|
->(v) { BLOWER_TYPE[v] } => [404],
|
@@ -514,6 +523,7 @@ module Aurora
|
|
514
523
|
->(v) { PUMP_TYPE[v] } => [413],
|
515
524
|
->(v) { PHASE_TYPE[v] } => [416],
|
516
525
|
method(:iz2_fan_desired) => [565],
|
526
|
+
->(v) { v == 0xffff ? 0 : v } => 601..699,
|
517
527
|
->(registers, idx) { to_string(registers, idx, 8) } => [710],
|
518
528
|
->(v) { COMPONENT_STATUS[v] } => [800, 803, 806, 812, 815, 818, 824, 827],
|
519
529
|
method(:axb_inputs) => [1103],
|
@@ -521,12 +531,13 @@ module Aurora
|
|
521
531
|
->(v) { TO_TENTHS.call(NEGATABLE.call(v)) } => [1135, 1136],
|
522
532
|
method(:to_int32) => [1146, 1148, 1150, 1152, 1154, 1156, 1164, 3422, 3424],
|
523
533
|
method(:manual_operation) => [3002],
|
534
|
+
method(:thermostat_configuration2) => [12_006],
|
524
535
|
->(v) { HEATING_MODE[v] } => [12_606, 21_202, 21_211, 21_220, 21_229, 21_238, 21_247],
|
525
536
|
->(v) { FAN_MODE[v] } => [12_621, 21_205, 21_214, 21_223, 21_232, 21_241, 21_250],
|
526
537
|
->(v) { from_bitmask(v, HUMIDIFIER_SETTINGS) } => [31_109],
|
527
538
|
->(v) { { humidification_target: v >> 8, dehumidification_target: v & 0xff } } => [31_110],
|
528
539
|
method(:iz2_demand) => [31_005],
|
529
|
-
method(:zone_configuration1) => [31_008, 31_011, 31_014, 31_017, 31_020, 31_023],
|
540
|
+
method(:zone_configuration1) => [12_005, 31_008, 31_011, 31_014, 31_017, 31_020, 31_023],
|
530
541
|
method(:zone_configuration2) => [31_009, 31_012, 31_015, 31_018, 31_021, 31_024],
|
531
542
|
method(:zone_configuration3) => [31_200, 31_203, 31_206, 31_209, 31_212, 31_215],
|
532
543
|
->(registers, idx) { to_string(registers, idx, 13) } => [31_400],
|
@@ -876,10 +887,14 @@ module Aurora
|
|
876
887
|
3904 => "VS Drive Leaving Air Temperature?",
|
877
888
|
3905 => "VS Drive Saturated Evaporator Discharge Temperature",
|
878
889
|
3906 => "VS Drive SuperHeat Temperature",
|
890
|
+
12_005 => "Fan Configuration",
|
891
|
+
12_006 => "Heating Mode",
|
879
892
|
12_606 => "Heating Mode (write)",
|
880
893
|
12_619 => "Heating Setpoint (write)",
|
881
894
|
12_620 => "Cooling Setpoint (write)",
|
882
895
|
12_621 => "Fan Mode (write)",
|
896
|
+
12_622 => "Intermittent Fan On Time (write)",
|
897
|
+
12_623 => "Intermittent Fan Off Time (write)",
|
883
898
|
31_003 => "Outdoor Temp",
|
884
899
|
31_005 => "IZ2 Demand",
|
885
900
|
31_109 => "Humidifier Mode", # write to 21114
|
data/lib/aurora/thermostat.rb
CHANGED
@@ -5,15 +5,39 @@ require "aurora/component"
|
|
5
5
|
module Aurora
|
6
6
|
class Thermostat < Component
|
7
7
|
attr_reader :target_mode,
|
8
|
+
:current_mode,
|
8
9
|
:target_fan_mode,
|
10
|
+
:current_fan_mode,
|
9
11
|
:ambient_temperature,
|
10
12
|
:cooling_target_temperature,
|
11
|
-
:heating_target_temperature
|
13
|
+
:heating_target_temperature,
|
14
|
+
:fan_intermittent_on,
|
15
|
+
:fan_intermittent_off
|
16
|
+
|
17
|
+
def registers_to_read
|
18
|
+
[31, 502, 745..746, 12_005..12_006]
|
19
|
+
end
|
12
20
|
|
13
21
|
def refresh(registers)
|
14
22
|
@ambient_temperature = registers[502]
|
15
23
|
@heating_target_temperature = registers[745]
|
16
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]
|
31
|
+
|
32
|
+
inputs = registers[31]
|
33
|
+
@current_fan_mode = inputs.include?(:g)
|
34
|
+
@current_mode = if inputs[:y2]
|
35
|
+
inputs[:o] ? :c2 : :h2
|
36
|
+
elsif inputs[:y1]
|
37
|
+
inputs[:o] ? :c1 : :h1
|
38
|
+
else
|
39
|
+
:standby
|
40
|
+
end
|
17
41
|
end
|
18
42
|
|
19
43
|
def target_mode=(value)
|
@@ -46,10 +70,18 @@ module Aurora
|
|
46
70
|
@cooling_target_temperature = value
|
47
71
|
end
|
48
72
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
73
|
+
def fan_intermittent_on=(value)
|
74
|
+
return unless value >= 0 && value <= 25 && (value % 5).zero?
|
75
|
+
|
76
|
+
holding_registers[12_622] = value
|
77
|
+
@fan_intermittent_on = value
|
78
|
+
end
|
79
|
+
|
80
|
+
def fan_intermittent_off=(value)
|
81
|
+
return unless value >= 0 && value <= 40 && (value % 5).zero?
|
82
|
+
|
83
|
+
holding_registers[12_623] = value
|
84
|
+
@fan_intermittent_off = value
|
53
85
|
end
|
54
86
|
end
|
55
87
|
end
|
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.5.3
|
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
|
@@ -136,11 +136,15 @@ files:
|
|
136
136
|
- lib/aurora/abc_client.rb
|
137
137
|
- lib/aurora/blower.rb
|
138
138
|
- lib/aurora/component.rb
|
139
|
+
- lib/aurora/compressor.rb
|
139
140
|
- lib/aurora/core_ext/string.rb
|
141
|
+
- lib/aurora/dhw.rb
|
142
|
+
- lib/aurora/humidifier.rb
|
140
143
|
- lib/aurora/iz2_zone.rb
|
141
144
|
- lib/aurora/mock_abc.rb
|
142
145
|
- lib/aurora/modbus/server.rb
|
143
146
|
- lib/aurora/modbus/slave.rb
|
147
|
+
- lib/aurora/mqtt_modbus.rb
|
144
148
|
- lib/aurora/pump.rb
|
145
149
|
- lib/aurora/registers.rb
|
146
150
|
- lib/aurora/thermostat.rb
|