waterfurnace_aurora 0.2.0 → 0.2.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_fetch +26 -0
- data/{bin → exe}/aurora_mock +23 -20
- data/{bin → exe}/aurora_monitor +21 -18
- data/{bin → exe}/aurora_mqtt_bridge +47 -47
- data/{bin → exe}/registers.yml +0 -0
- data/lib/aurora/abc_client.rb +39 -35
- data/lib/aurora/iz2_zone.rb +40 -30
- data/lib/aurora/modbus/server.rb +22 -15
- data/lib/aurora/modbus/slave.rb +27 -20
- data/lib/aurora/registers.rb +206 -202
- data/lib/aurora/version.rb +3 -1
- data/lib/aurora.rb +8 -6
- data/lib/waterfurnace_aurora.rb +3 -1
- metadata +26 -23
- data/bin/aurora_fetch +0 -25
data/lib/aurora/iz2_zone.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aurora
|
2
4
|
class IZ2Zone
|
3
5
|
attr_reader :zone_number,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
:target_mode,
|
7
|
+
:current_mode,
|
8
|
+
:target_fan_mode,
|
9
|
+
:current_fan_mode,
|
10
|
+
:fan_intermittent_on,
|
11
|
+
:fan_intermittent_off,
|
12
|
+
:priority,
|
13
|
+
:size, :normalized_size,
|
14
|
+
:ambient_temperature,
|
15
|
+
:cooling_target_temperature,
|
16
|
+
:heating_target_temperature
|
15
17
|
|
16
18
|
def initialize(abc, zone_number)
|
17
19
|
@abc = abc
|
@@ -19,11 +21,11 @@ module Aurora
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def refresh(registers)
|
22
|
-
@ambient_temperature = registers[
|
24
|
+
@ambient_temperature = registers[31_007 + (zone_number - 1) * 3]
|
23
25
|
|
24
|
-
config1 = registers[
|
25
|
-
config2 = registers[
|
26
|
-
config3 = registers[
|
26
|
+
config1 = registers[31_008 + (zone_number - 1) * 3]
|
27
|
+
config2 = registers[31_009 + (zone_number - 1) * 3]
|
28
|
+
config3 = registers[31_200 + (zone_number - 1) * 3]
|
27
29
|
|
28
30
|
@target_fan_mode = config1[:fan]
|
29
31
|
@fan_intermittent_on = config1[:on_time]
|
@@ -42,41 +44,46 @@ module Aurora
|
|
42
44
|
def target_mode=(value)
|
43
45
|
value = Aurora::HEATING_MODE.invert[value]
|
44
46
|
return unless value
|
45
|
-
|
46
|
-
@
|
47
|
+
|
48
|
+
@abc.modbus_slave.holding_registers[21_202 + (zone_number - 1) * 9] = value
|
49
|
+
@target_mode = Aurora::HEATING_MODE[@abc.modbus_slave.holding_registers[21_202 + (zone_number - 1) * 9]]
|
47
50
|
end
|
48
51
|
|
49
52
|
def target_fan_mode=(value)
|
50
53
|
value = Aurora::FAN_MODE.invert[value]
|
51
54
|
return unless value
|
52
|
-
|
53
|
-
|
55
|
+
|
56
|
+
@abc.modbus_slave.holding_registers[21_205 + (zone_number - 1) * 9] = value
|
57
|
+
registers = @abc.modbus_slave.read_multiple_holding_registers(31_008 + (zone_number - 1) * 3)
|
54
58
|
Aurora.transform_registers(registers)
|
55
59
|
@target_fan_mode = registers.first.last[:fan]
|
56
60
|
end
|
57
61
|
|
58
62
|
def fan_intermittent_on=(value)
|
59
|
-
return unless value >= 0 && value <= 25 && value % 5
|
60
|
-
|
61
|
-
|
63
|
+
return unless value >= 0 && value <= 25 && (value % 5).zero?
|
64
|
+
|
65
|
+
@abc.modbus_slave.holding_registers[21_206 + (zone_number - 1) * 9] = value
|
66
|
+
registers = @abc.modbus_slave.read_multiple_holding_registers(31_008 + (zone_number - 1) * 3)
|
62
67
|
Aurora.transform_registers(registers)
|
63
68
|
@fan_intermittent_on = registers.first.last[:on_time]
|
64
69
|
end
|
65
70
|
|
66
71
|
def fan_intermittent_off=(value)
|
67
|
-
return unless value >= 0 && value <= 40 && value % 5
|
68
|
-
|
69
|
-
|
72
|
+
return unless value >= 0 && value <= 40 && (value % 5).zero?
|
73
|
+
|
74
|
+
@abc.modbus_slave.holding_registers[21_207 + (zone_number - 1) * 9] = value
|
75
|
+
registers = @abc.modbus_slave.read_multiple_holding_registers(31_008 + (zone_number - 1) * 3)
|
70
76
|
Aurora.transform_registers(registers)
|
71
77
|
@fan_intermittent_on = registers.first.last[:off_time]
|
72
78
|
end
|
73
79
|
|
74
80
|
def heating_target_temperature=(value)
|
75
81
|
return unless value >= 40 && value <= 90
|
82
|
+
|
76
83
|
value = (value * 10).to_i
|
77
|
-
@abc.modbus_slave.holding_registers[
|
84
|
+
@abc.modbus_slave.holding_registers[21_203 + (zone_number - 1) * 9] = value
|
78
85
|
|
79
|
-
base =
|
86
|
+
base = 31_008 + (zone_number - 1) * 3
|
80
87
|
registers = @abc.modbus_slave.read_multiple_holding_registers(base..(base + 1))
|
81
88
|
Aurora.transform_registers(registers)
|
82
89
|
registers[base + 1][:heating_target_temperature]
|
@@ -84,16 +91,19 @@ module Aurora
|
|
84
91
|
|
85
92
|
def cooling_target_temperature=(value)
|
86
93
|
return unless value >= 54 && value <= 99
|
94
|
+
|
87
95
|
value = (value * 10).to_i
|
88
|
-
@abc.modbus_slave.holding_registers[
|
96
|
+
@abc.modbus_slave.holding_registers[21_204 + (zone_number - 1) * 9] = value
|
89
97
|
|
90
|
-
registers = @abc.modbus_slave.read_multiple_holding_registers(
|
98
|
+
registers = @abc.modbus_slave.read_multiple_holding_registers(31_008 + (zone_number - 1) * 3)
|
91
99
|
Aurora.transform_registers(registers)
|
92
100
|
registers.first.last[:cooling_target_temperature]
|
93
101
|
end
|
94
102
|
|
95
103
|
def inspect
|
96
|
-
"#<Aurora::IZ2Zone #{(instance_variables - [:@abc]).map
|
104
|
+
"#<Aurora::IZ2Zone #{(instance_variables - [:@abc]).map do |iv|
|
105
|
+
"#{iv}=#{instance_variable_get(iv).inspect}"
|
106
|
+
end.join(', ')}>"
|
97
107
|
end
|
98
108
|
end
|
99
109
|
end
|
data/lib/aurora/modbus/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aurora
|
2
4
|
module ModBus
|
3
5
|
module Server
|
@@ -5,26 +7,30 @@ module Aurora
|
|
5
7
|
case func
|
6
8
|
when 65
|
7
9
|
# 1 function register, a multiple of two words
|
8
|
-
return unless (req.length - 1) % 4
|
10
|
+
return unless ((req.length - 1) % 4).zero?
|
11
|
+
|
9
12
|
params = []
|
10
|
-
req[1
|
13
|
+
req[1..].unpack("n*").each_slice(2) do |(addr, quant)|
|
11
14
|
params << { addr: addr, quant: quant }
|
12
15
|
end
|
13
16
|
params
|
14
17
|
when 66
|
15
|
-
return unless (req.length - 1) % 2
|
16
|
-
|
18
|
+
return unless ((req.length - 1) % 2).zero?
|
19
|
+
|
20
|
+
req[1..].unpack("n*")
|
17
21
|
when 67
|
18
22
|
# 1 function register, a multiple of two words
|
19
|
-
return unless (req.length - 1) % 4
|
23
|
+
return unless ((req.length - 1) % 4).zero?
|
24
|
+
|
20
25
|
params = []
|
21
|
-
req[1
|
26
|
+
req[1..].unpack("n*").each_slice(2) do |(addr, val)|
|
22
27
|
params << { addr: addr, val: val }
|
23
28
|
end
|
24
29
|
params
|
25
30
|
when 68
|
26
31
|
return unless req.length == 5
|
27
|
-
|
32
|
+
|
33
|
+
{ noidea1: req[1, 2].unpack("n"), noidea2: req[3, 2].unpack("n") }
|
28
34
|
else
|
29
35
|
super
|
30
36
|
end
|
@@ -33,7 +39,8 @@ module Aurora
|
|
33
39
|
def parse_response(func, res)
|
34
40
|
return {} if func == 67 && res.length == 1
|
35
41
|
return { noidea: res[-1].ord } if func == 68 && res.length == 2
|
36
|
-
|
42
|
+
|
43
|
+
func = 3 if [65, 66].include?(func)
|
37
44
|
super
|
38
45
|
end
|
39
46
|
|
@@ -46,17 +53,17 @@ module Aurora
|
|
46
53
|
return (func | 0x80).chr + err.chr
|
47
54
|
end
|
48
55
|
|
49
|
-
pdu += slave.holding_registers[param[:addr],param[:quant]].pack(
|
56
|
+
pdu += slave.holding_registers[param[:addr], param[:quant]].pack("n*")
|
50
57
|
end
|
51
|
-
|
52
|
-
|
58
|
+
func.chr + pdu.length.chr + pdu
|
59
|
+
|
53
60
|
when 66
|
54
|
-
pdu = params.map { |addr| slave.holding_registers[addr] }.pack(
|
55
|
-
|
56
|
-
|
61
|
+
pdu = params.map { |addr| slave.holding_registers[addr] }.pack("n*")
|
62
|
+
func.chr + pdu.length.chr + pdu
|
63
|
+
|
57
64
|
when 67
|
58
65
|
slave.holding_registers[param[:addr]] = param[:val]
|
59
|
-
pdu = req[0,2]
|
66
|
+
pdu = req[0, 2]
|
60
67
|
else
|
61
68
|
super
|
62
69
|
end
|
data/lib/aurora/modbus/slave.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aurora
|
2
4
|
module ModBus
|
3
5
|
module Slave
|
4
6
|
def read_multiple_holding_registers(*ranges)
|
5
7
|
values = if ranges.any? { |r| r.is_a?(Range) }
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
addrs_and_lengths = ranges.map do |r|
|
9
|
+
r = Array(r)
|
10
|
+
[r.first, r.last - r.first + 1]
|
11
|
+
end.flatten
|
12
|
+
query("A#{addrs_and_lengths.pack('n*')}").unpack("n*")
|
13
|
+
else
|
14
|
+
query("B#{ranges.pack('n*')}").unpack("n*")
|
15
|
+
end
|
11
16
|
ranges.map { |r| Array(r) }.flatten.zip(values).to_h
|
12
17
|
end
|
13
18
|
|
@@ -19,6 +24,7 @@ module Aurora
|
|
19
24
|
class WFProxy < ::ModBus::ReadWriteProxy
|
20
25
|
def [](*keys)
|
21
26
|
return super if keys.length == 1
|
27
|
+
|
22
28
|
@slave.read_multiple_holding_registers(*keys)
|
23
29
|
end
|
24
30
|
end
|
@@ -28,24 +34,25 @@ module Aurora
|
|
28
34
|
# Read the slave_id and function code
|
29
35
|
msg = read(io, 2)
|
30
36
|
log logging_bytes(msg)
|
31
|
-
|
37
|
+
|
32
38
|
function_code = msg.getbyte(1)
|
33
39
|
case function_code
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
when 1, 2, 3, 4, 65, 66
|
41
|
+
# read the third byte to find out how much more
|
42
|
+
# we need to read + CRC
|
43
|
+
msg += read(io, 1)
|
44
|
+
msg += read(io, msg.getbyte(2) + 2)
|
45
|
+
when 5, 6, 15, 16
|
46
|
+
# We just read in an additional 6 bytes
|
47
|
+
msg += read(io, 6)
|
48
|
+
when 22
|
49
|
+
msg += read(io, 8)
|
50
|
+
when 0x80..0xff
|
51
|
+
msg += read(io, 3)
|
52
|
+
else
|
53
|
+
raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
|
48
54
|
end
|
55
|
+
msg
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
data/lib/aurora/registers.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aurora
|
2
|
-
|
4
|
+
module_function
|
3
5
|
|
4
6
|
def normalize_ranges(ranges)
|
5
7
|
registers = ranges.map { |r| Array(r) }.flatten.sort.uniq
|
@@ -9,26 +11,26 @@ module Aurora
|
|
9
11
|
count = 0
|
10
12
|
registers.each_with_index do |r, i|
|
11
13
|
run_start ||= r
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
count += range.count
|
28
|
-
result << range
|
14
|
+
next unless i + 1 == registers.length || r + 1 != registers[i + 1]
|
15
|
+
|
16
|
+
if r == run_start
|
17
|
+
result << r
|
18
|
+
if (count += 1) == 100
|
19
|
+
totals << result
|
20
|
+
result = []
|
21
|
+
count = 0
|
22
|
+
end
|
23
|
+
else
|
24
|
+
range = run_start..r
|
25
|
+
if count + range.count > 100
|
26
|
+
totals << result
|
27
|
+
result = []
|
28
|
+
count = 0
|
29
29
|
end
|
30
|
-
|
30
|
+
count += range.count
|
31
|
+
result << range
|
31
32
|
end
|
33
|
+
run_start = nil
|
32
34
|
end
|
33
35
|
totals << result unless result.empty?
|
34
36
|
totals
|
@@ -39,13 +41,13 @@ module Aurora
|
|
39
41
|
TO_LAST_LOCKOUT = ->(v) { v & 0x8000 == 0x8000 ? v & 0x7fff : nil }
|
40
42
|
NEGATABLE = ->(v) { v & 0x8000 == 0x8000 ? v - 0x10000 : v }
|
41
43
|
|
42
|
-
def from_bitmask(
|
44
|
+
def from_bitmask(value, flags)
|
43
45
|
result = []
|
44
46
|
flags.each do |(bit, flag)|
|
45
|
-
result << flag if
|
46
|
-
|
47
|
+
result << flag if value & bit == bit
|
48
|
+
value &= ~bit
|
47
49
|
end
|
48
|
-
result << "0x%04x"
|
50
|
+
result << format("0x%04x", value) unless value.zero?
|
49
51
|
result
|
50
52
|
end
|
51
53
|
|
@@ -53,7 +55,7 @@ module Aurora
|
|
53
55
|
puts "converting #{idx} of length #{length}"
|
54
56
|
(idx...(idx + length)).map do |i|
|
55
57
|
(registers[i] >> 8).chr + (registers[i] & 0xff).chr
|
56
|
-
end.join.sub(/[ \0]+$/,
|
58
|
+
end.join.sub(/[ \0]+$/, "")
|
57
59
|
end
|
58
60
|
|
59
61
|
FAULTS = {
|
@@ -106,26 +108,27 @@ module Aurora
|
|
106
108
|
75 => "Charge Loss",
|
107
109
|
76 => "Suction Temperatur Sensor Limit",
|
108
110
|
77 => "Leaving Air Temperature Sensor Limit",
|
109
|
-
78 => "Maximum Operating Pressure Limit"
|
110
|
-
}
|
111
|
+
78 => "Maximum Operating Pressure Limit"
|
112
|
+
}.freeze
|
111
113
|
|
112
114
|
AR_SETTINGS = {
|
113
115
|
0 => "Cycle with Compressor",
|
114
116
|
1 => "Cycle with Thermostat Humidification Call",
|
115
117
|
2 => "Slow Opening Water Valve",
|
116
118
|
3 => "Cycle with Blower"
|
117
|
-
}
|
119
|
+
}.freeze
|
120
|
+
|
121
|
+
def dipswitch_settings(value)
|
122
|
+
return :manual if value == 0x7fff
|
118
123
|
|
119
|
-
def dipswitch_settings(v)
|
120
|
-
return :manual if v == 0x7fff
|
121
124
|
{
|
122
|
-
fp1:
|
123
|
-
fp2:
|
124
|
-
rv:
|
125
|
-
ar: AR_SETTINGS[(
|
126
|
-
cc:
|
127
|
-
lo:
|
128
|
-
dh_rh:
|
125
|
+
fp1: value & 0x01 == 0x01 ? "30ºF" : "15ºF",
|
126
|
+
fp2: value & 0x02 == 0x02 ? "30ºF" : "15ºF",
|
127
|
+
rv: value & 0x04 == 0x04 ? "O" : "B",
|
128
|
+
ar: AR_SETTINGS[(value >> 3) & 0x7],
|
129
|
+
cc: value & 0x20 == 0x20 ? "Single Stage" : "Dual Stage",
|
130
|
+
lo: value & 0x40 == 0x40 ? "Continouous" : "Pulse",
|
131
|
+
dh_rh: value & 0x80 == 0x80 ? "Dehumdifier On" : "Reheat On"
|
129
132
|
}
|
130
133
|
end
|
131
134
|
|
@@ -138,8 +141,8 @@ module Aurora
|
|
138
141
|
0x20 => :eh2,
|
139
142
|
0x200 => :accessory,
|
140
143
|
0x400 => :lockout,
|
141
|
-
0x800 => :alarm
|
142
|
-
}
|
144
|
+
0x800 => :alarm
|
145
|
+
}.freeze
|
143
146
|
|
144
147
|
SYSTEM_INPUTS = {
|
145
148
|
0x01 => "Y1",
|
@@ -149,18 +152,18 @@ module Aurora
|
|
149
152
|
0x10 => "G",
|
150
153
|
0x20 => "Dehumidifer",
|
151
154
|
0x40 => "Emergency Shutdown",
|
152
|
-
0x200 => "Load Shed"
|
153
|
-
}
|
155
|
+
0x200 => "Load Shed"
|
156
|
+
}.freeze
|
154
157
|
|
155
|
-
def status(
|
158
|
+
def status(value)
|
156
159
|
result = {
|
157
|
-
lps:
|
158
|
-
hps:
|
160
|
+
lps: value & 0x80 == 0x80 ? :closed : :open,
|
161
|
+
hps: value & 0x100 == 0x100 ? :closed : :open
|
159
162
|
}
|
160
|
-
result[:load_shed] = true if
|
161
|
-
result[:emergency_shutdown] = true if
|
162
|
-
leftover =
|
163
|
-
result[:unknown] = "0x%04x"
|
163
|
+
result[:load_shed] = true if value & 0x0200 == 0x0200
|
164
|
+
result[:emergency_shutdown] = true if value & 0x0040 == 0x0040
|
165
|
+
leftover = value & ~0x03c0
|
166
|
+
result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
|
164
167
|
result
|
165
168
|
end
|
166
169
|
|
@@ -169,18 +172,18 @@ module Aurora
|
|
169
172
|
0x04 => "Low Suction Pressure",
|
170
173
|
0x10 => "Low Discharge Pressure",
|
171
174
|
0x20 => "High Discharge Pressure",
|
172
|
-
0x40 => "Output Power Limit"
|
173
|
-
}
|
175
|
+
0x40 => "Output Power Limit"
|
176
|
+
}.freeze
|
174
177
|
|
175
178
|
VS_SAFE_MODE = {
|
176
179
|
0x01 => "EEV Indoor Failed",
|
177
180
|
0x02 => "EEV Outdoor Failed",
|
178
|
-
0x04 => "Invalid Ambient Temp"
|
179
|
-
}
|
181
|
+
0x04 => "Invalid Ambient Temp"
|
182
|
+
}.freeze
|
180
183
|
|
181
184
|
VS_ALARM1 = {
|
182
|
-
0x8000 => "Internal Error"
|
183
|
-
}
|
185
|
+
0x8000 => "Internal Error"
|
186
|
+
}.freeze
|
184
187
|
|
185
188
|
VS_ALARM2 = {
|
186
189
|
0x0001 => "Multi Safe Modes",
|
@@ -196,24 +199,24 @@ module Aurora
|
|
196
199
|
0x0400 => "DC Under Voltage",
|
197
200
|
0x0800 => "Invalid Suction Pressure",
|
198
201
|
0x1000 => "Invalid Discharge Pressure",
|
199
|
-
0x2000 => "Low Discharge Pressure"
|
200
|
-
}
|
202
|
+
0x2000 => "Low Discharge Pressure"
|
203
|
+
}.freeze
|
201
204
|
|
202
205
|
VS_EEV2 = {
|
203
206
|
0x0010 => "Invalid Suction Temperature",
|
204
207
|
0x0020 => "Invalid Leaving Air Temperature",
|
205
|
-
0x0040 => "Invalid Suction Pressure"
|
206
|
-
|
207
|
-
}
|
208
|
+
0x0040 => "Invalid Suction Pressure"
|
209
|
+
|
210
|
+
}.freeze
|
208
211
|
|
209
212
|
AXB_INPUTS = {
|
210
|
-
}
|
213
|
+
}.freeze
|
211
214
|
|
212
215
|
AXB_OUTPUTS = {
|
213
216
|
0x10 => "Accessory 2",
|
214
217
|
0x02 => "Loop Pump",
|
215
218
|
0x01 => "DHW"
|
216
|
-
}
|
219
|
+
}.freeze
|
217
220
|
|
218
221
|
HEATING_MODE = {
|
219
222
|
0 => :off,
|
@@ -221,30 +224,30 @@ module Aurora
|
|
221
224
|
2 => :cool,
|
222
225
|
3 => :heat,
|
223
226
|
4 => :eheat
|
224
|
-
}
|
227
|
+
}.freeze
|
225
228
|
|
226
229
|
FAN_MODE = {
|
227
230
|
0 => :auto,
|
228
231
|
1 => :continuous,
|
229
232
|
2 => :intermittent
|
230
|
-
}
|
233
|
+
}.freeze
|
231
234
|
|
232
235
|
HUMIDIFIER_SETTINGS = {
|
233
236
|
0x4000 => :auto_dehumidification,
|
234
|
-
0x8000 => :auto_humidification
|
235
|
-
}
|
237
|
+
0x8000 => :auto_humidification
|
238
|
+
}.freeze
|
236
239
|
|
237
240
|
INVERSE_HUMIDIFIER_SETTINGS = {
|
238
241
|
0x4000 => :manual_dehumidification,
|
239
|
-
0x8000 => :manual_humidification
|
240
|
-
}
|
242
|
+
0x8000 => :manual_humidification
|
243
|
+
}.freeze
|
241
244
|
|
242
245
|
ZONE_SIZES = {
|
243
246
|
0 => 0,
|
244
247
|
1 => 25,
|
245
248
|
2 => 45,
|
246
|
-
3 => 70
|
247
|
-
}
|
249
|
+
3 => 70
|
250
|
+
}.freeze
|
248
251
|
|
249
252
|
CALLS = {
|
250
253
|
0x0 => :standby,
|
@@ -254,39 +257,39 @@ module Aurora
|
|
254
257
|
0x4 => :h3,
|
255
258
|
0x5 => :c1,
|
256
259
|
0x6 => :c2,
|
257
|
-
0x7 => :unknown7
|
258
|
-
}
|
260
|
+
0x7 => :unknown7
|
261
|
+
}.freeze
|
259
262
|
|
260
|
-
def iz2_demand(
|
263
|
+
def iz2_demand(value)
|
261
264
|
{
|
262
|
-
fan_demand:
|
263
|
-
unit_demand:
|
265
|
+
fan_demand: value >> 8,
|
266
|
+
unit_demand: value & 0xff
|
264
267
|
}
|
265
268
|
end
|
266
269
|
|
267
|
-
def zone_configuration1(
|
268
|
-
fan = if
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
270
|
+
def zone_configuration1(value)
|
271
|
+
fan = if value & 0x80 == 0x80
|
272
|
+
:continuous
|
273
|
+
elsif value & 0x100 == 0x100
|
274
|
+
:intermittent
|
275
|
+
else
|
276
|
+
:auto
|
277
|
+
end
|
275
278
|
result = {
|
276
279
|
fan: fan,
|
277
|
-
on_time: ((
|
278
|
-
off_time: (((
|
279
|
-
cooling_target_temperature: ((
|
280
|
-
heating_target_temperature_carry:
|
280
|
+
on_time: ((value >> 9) & 0x7) * 5,
|
281
|
+
off_time: (((value >> 12) & 0x7) + 1) * 5,
|
282
|
+
cooling_target_temperature: ((value & 0x7e) >> 1) + 36,
|
283
|
+
heating_target_temperature_carry: value & 0o1
|
281
284
|
}
|
282
|
-
leftover =
|
283
|
-
result[:unknown] = "0x%04x"
|
285
|
+
leftover = value & ~0x7fff
|
286
|
+
result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
|
284
287
|
result
|
285
288
|
end
|
286
289
|
|
287
|
-
def zone_configuration2(registers,
|
288
|
-
prior_v = registers[
|
289
|
-
v = registers[
|
290
|
+
def zone_configuration2(registers, key)
|
291
|
+
prior_v = registers[key - 1] if registers.key?(key - 1)
|
292
|
+
v = registers[key]
|
290
293
|
result = {
|
291
294
|
call: CALLS[(v >> 1) & 0x7],
|
292
295
|
mode: HEATING_MODE[(v >> 8) & 0x03],
|
@@ -297,20 +300,20 @@ module Aurora
|
|
297
300
|
result[:heating_target_temperature] = ((carry << 5) | ((v & 0xf800) >> 11)) + 36
|
298
301
|
end
|
299
302
|
leftover = v & ~0xfb1e
|
300
|
-
result[:unknown] = "0x%04x"
|
303
|
+
result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
|
301
304
|
result
|
302
305
|
end
|
303
306
|
|
304
307
|
# hi order byte is normalized zone size
|
305
|
-
def zone_configuration3(
|
306
|
-
size = (
|
308
|
+
def zone_configuration3(value)
|
309
|
+
size = (value >> 3) & 0x3
|
307
310
|
result = {
|
308
|
-
zone_priority: (
|
311
|
+
zone_priority: (value & 0x20) == 0x20 ? :economy : :comfort,
|
309
312
|
zone_size: ZONE_SIZES[size],
|
310
|
-
normalized_size:
|
313
|
+
normalized_size: value >> 8
|
311
314
|
}
|
312
|
-
leftover =
|
313
|
-
result[:unknown] = "0x%04x"
|
315
|
+
leftover = value & ~0xff38
|
316
|
+
result[:unknown] = format("0x%04x", leftover) unless leftover.zero?
|
314
317
|
result
|
315
318
|
end
|
316
319
|
|
@@ -321,14 +324,14 @@ module Aurora
|
|
321
324
|
TO_HUNDREDTHS => [2, 3, 807, 813, 816, 817, 819, 820, 825, 828],
|
322
325
|
method(:dipswitch_settings) => [4, 33],
|
323
326
|
TO_TENTHS => [19, 20, 401, 567, 740, 745, 746, 900, 1105, 1106, 1107, 1108, 1110, 1111, 1114, 1117, 1134, 1136,
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
327
|
+
21_203, 21_204,
|
328
|
+
21_212, 21_213,
|
329
|
+
21_221, 21_222,
|
330
|
+
21_230, 22_131,
|
331
|
+
21_239, 21_240,
|
332
|
+
21_248, 21_249,
|
333
|
+
31_003,
|
334
|
+
31_007, 31_010, 31_013, 31_016, 31_019, 31_022],
|
332
335
|
TO_LAST_LOCKOUT => [26],
|
333
336
|
->(v) { from_bitmask(v, SYSTEM_OUTPUTS) } => [27, 30],
|
334
337
|
->(v) { from_bitmask(v, SYSTEM_INPUTS) } => [28],
|
@@ -345,41 +348,41 @@ module Aurora
|
|
345
348
|
->(v) { from_bitmask(v, AXB_INPUTS) } => [1103],
|
346
349
|
->(v) { from_bitmask(v, AXB_OUTPUTS) } => [1104],
|
347
350
|
->(v) { TO_TENTHS.call(NEGATABLE.call(v)) } => [1136],
|
348
|
-
->(v) { HEATING_MODE[v] } => [
|
349
|
-
->(v) { FAN_MODE[v] } => [
|
350
|
-
->(v) { from_bitmask(v, HUMIDIFIER_SETTINGS) } => [
|
351
|
-
->(v) { { humidification_target: v >> 8, dehumidification_target: v & 0xff } } => [
|
352
|
-
method(:iz2_demand) => [
|
353
|
-
method(:zone_configuration1) => [
|
354
|
-
method(:zone_configuration2) => [
|
355
|
-
method(:zone_configuration3) => [
|
356
|
-
->(registers, idx) { to_string(registers, idx, 13) } => [
|
357
|
-
->(registers, idx) { to_string(registers, idx, 8) } => [
|
358
|
-
->(registers, idx) { to_string(registers, idx, 13) } => [
|
359
|
-
->(registers, idx) { to_string(registers, idx, 13) } => [
|
360
|
-
->(registers, idx) { to_string(registers, idx, 13) } => [
|
361
|
-
->(registers, idx) { to_string(registers, idx, 13) } => [
|
362
|
-
}
|
351
|
+
->(v) { HEATING_MODE[v] } => [21_202, 21_211, 21_220, 21_229, 21_238, 21_247],
|
352
|
+
->(v) { FAN_MODE[v] } => [21_205, 21_214, 21_223, 21_232, 21_241, 21_250],
|
353
|
+
->(v) { from_bitmask(v, HUMIDIFIER_SETTINGS) } => [31_109],
|
354
|
+
->(v) { { humidification_target: v >> 8, dehumidification_target: v & 0xff } } => [31_110],
|
355
|
+
method(:iz2_demand) => [31_005],
|
356
|
+
method(:zone_configuration1) => [31_008, 31_011, 31_014, 31_017, 31_020, 31_023],
|
357
|
+
method(:zone_configuration2) => [31_009, 31_012, 31_015, 31_018, 31_021, 31_024],
|
358
|
+
method(:zone_configuration3) => [31_200, 31_203, 31_206, 31_209, 31_212, 31_215],
|
359
|
+
->(registers, idx) { to_string(registers, idx, 13) } => [31_400],
|
360
|
+
->(registers, idx) { to_string(registers, idx, 8) } => [31_413],
|
361
|
+
->(registers, idx) { to_string(registers, idx, 13) } => [31_421],
|
362
|
+
->(registers, idx) { to_string(registers, idx, 13) } => [31_434],
|
363
|
+
->(registers, idx) { to_string(registers, idx, 13) } => [31_447],
|
364
|
+
->(registers, idx) { to_string(registers, idx, 13) } => [31_460]
|
365
|
+
}.freeze
|
363
366
|
|
364
367
|
REGISTER_FORMATS = {
|
365
368
|
"%ds" => [1, 6, 9, 15, 84, 85],
|
366
369
|
"%dV" => [16, 112],
|
367
370
|
"%0.1fºF" => [19, 20, 401, 567, 740, 745, 746, 900, 1110, 1111, 1114, 1134, 1136,
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
371
|
+
21_203, 21_204,
|
372
|
+
21_212, 21_213,
|
373
|
+
21_221, 21_222,
|
374
|
+
21_230, 21_231,
|
375
|
+
21_239, 21_240,
|
376
|
+
21_248, 21_249,
|
377
|
+
31_003,
|
378
|
+
31_007, 31_010, 31_013, 31_016, 31_019, 31_022],
|
376
379
|
"E%d" => [25, 26],
|
377
380
|
"%d%%" => [282, 321, 322, 346, 565, 741],
|
378
381
|
"%0.1fA" => [1105, 1106, 1107, 1108],
|
379
382
|
"%0.1fgpm" => [1117],
|
380
383
|
"%dW" => [1147, 1149, 1151, 1153, 1165],
|
381
|
-
"%dBtuh" => [1157]
|
382
|
-
}
|
384
|
+
"%dBtuh" => [1157]
|
385
|
+
}.freeze
|
383
386
|
|
384
387
|
def ignore(range)
|
385
388
|
range.zip(Array.new(range.count)).to_h
|
@@ -391,9 +394,9 @@ module Aurora
|
|
391
394
|
|
392
395
|
def zone_registers
|
393
396
|
(1..6).map do |i|
|
394
|
-
base1 =
|
395
|
-
base2 =
|
396
|
-
base3 =
|
397
|
+
base1 = 21_202 + (i - 1) * 9
|
398
|
+
base2 = 31_007 + (i - 1) * 3
|
399
|
+
base3 = 31_200 + (i - 1) * 3
|
397
400
|
{
|
398
401
|
base1 => "Zone #{i} Heating Mode",
|
399
402
|
(base1 + 1) => "Zone #{i} Heating Setpoint (write)",
|
@@ -404,12 +407,12 @@ module Aurora
|
|
404
407
|
base2 => "Zone #{i} Ambient Temperature",
|
405
408
|
(base2 + 1) => "Zone #{i} Configuration 1",
|
406
409
|
(base2 + 2) => "Zone #{i} Configuration 2",
|
407
|
-
base3 => "Zone #{i} Configuration 3"
|
410
|
+
base3 => "Zone #{i} Configuration 3"
|
408
411
|
}
|
409
412
|
end.inject({}, &:merge)
|
410
413
|
end
|
411
414
|
|
412
|
-
WRITEABLE = [112, 340, 341, 342, 346, 347]
|
415
|
+
WRITEABLE = [112, 340, 341, 342, 346, 347].freeze
|
413
416
|
|
414
417
|
# these are the valid ranges (i.e. the ABC will return _some_ value)
|
415
418
|
# * means 6 sequential ranges of equal size (i.e. must be repeated for each
|
@@ -453,42 +456,42 @@ module Aurora
|
|
453
456
|
3800..3809,
|
454
457
|
3818..3834,
|
455
458
|
3900..3914,
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
]
|
459
|
+
12_000..12_019,
|
460
|
+
12_098..12_099,
|
461
|
+
12_100..12_119,
|
462
|
+
12_200..12_239,
|
463
|
+
12_300..12_319,
|
464
|
+
12_400..12_569,
|
465
|
+
12_600..12_639,
|
466
|
+
12_700..12_799,
|
467
|
+
20_000..20_099,
|
468
|
+
21_100..21_136,
|
469
|
+
21_200..21_265,
|
470
|
+
21_400..21_472,
|
471
|
+
21_500..21_589,
|
472
|
+
22_100..22_162, # *
|
473
|
+
22_200..22_262, # *
|
474
|
+
22_300..22_362, # *
|
475
|
+
22_400..22_462, # *
|
476
|
+
22_500..22_562, # *
|
477
|
+
22_600..22_662, # *
|
478
|
+
30_000..30_099,
|
479
|
+
31_000..31_034,
|
480
|
+
31_100..31_129,
|
481
|
+
31_200..31_229,
|
482
|
+
31_300..31_329,
|
483
|
+
31_400..31_472,
|
484
|
+
32_100..32_162, # *
|
485
|
+
32_200..32_262, # *
|
486
|
+
32_300..32_362, # *
|
487
|
+
32_400..32_462, # *
|
488
|
+
32_500..32_562, # *
|
489
|
+
32_600..32_662, # *
|
490
|
+
60_050..60_053,
|
491
|
+
60_100..60_109,
|
492
|
+
60_200..60_200,
|
493
|
+
61_000..61_009
|
494
|
+
].freeze
|
492
495
|
|
493
496
|
def read_all_registers(modbus_slave)
|
494
497
|
result = []
|
@@ -501,10 +504,10 @@ module Aurora
|
|
501
504
|
REGISTER_RANGES.map(&:to_a).flatten.zip(result).to_h
|
502
505
|
end
|
503
506
|
|
504
|
-
def diff_registers(
|
507
|
+
def diff_registers(lhs, rhs)
|
505
508
|
diff = {}
|
506
|
-
|
507
|
-
diff[k] = [
|
509
|
+
lhs.each_key do |k|
|
510
|
+
diff[k] = [lhs[k], rhs[k]] if lhs[k] != rhs[k]
|
508
511
|
end
|
509
512
|
diff
|
510
513
|
end
|
@@ -539,7 +542,7 @@ module Aurora
|
|
539
542
|
92 => "Model Number",
|
540
543
|
105 => "Serial Number",
|
541
544
|
112 => "Setup Line Voltage",
|
542
|
-
201 => "Discharge Pressure", # I can't figure out how this number is represented;
|
545
|
+
201 => "Discharge Pressure", # I can't figure out how this number is represented;
|
543
546
|
203 => "Suction Pressure",
|
544
547
|
205 => "Discharge Temperature",
|
545
548
|
207 => "Loop Entering Water Temperature",
|
@@ -565,7 +568,7 @@ module Aurora
|
|
565
568
|
344 => "ECM Speed",
|
566
569
|
346 => "Cooling Airflow Adjustment",
|
567
570
|
347 => "Aux Heat ECM Speed",
|
568
|
-
362 => "Active Dehumidify", # any value is true
|
571
|
+
362 => "Active Dehumidify", # any value is true
|
569
572
|
401 => "DHW Setpoint",
|
570
573
|
414 => "On Peak/SmartGrid 2", # 0x0001 only
|
571
574
|
483 => "Number of IZ2 Zones",
|
@@ -605,27 +608,27 @@ module Aurora
|
|
605
608
|
1157 => "Ht of Rej",
|
606
609
|
1165 => "VS Pump Watts",
|
607
610
|
3027 => "Compressor Speed",
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
}.merge(ignore(89..91))
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
611
|
+
31_003 => "Outdoor Temp",
|
612
|
+
31_005 => "IZ2 Demand",
|
613
|
+
31_109 => "Humidifier Mode", # write to 21114
|
614
|
+
31_110 => "Manual De/Humidification Target", # write to 21115
|
615
|
+
31_400 => "Dealer Name",
|
616
|
+
31_413 => "Dealer Phone",
|
617
|
+
31_421 => "Dealer Address 1",
|
618
|
+
31_434 => "Dealer Address 2",
|
619
|
+
31_447 => "Dealer Email",
|
620
|
+
31_460 => "Dealer Website"
|
621
|
+
}.merge(ignore(89..91))
|
622
|
+
.merge(ignore(93..104))
|
623
|
+
.merge(ignore(106..109))
|
624
|
+
.merge(faults(601..699))
|
625
|
+
.merge(zone_registers)
|
626
|
+
.merge(ignore(31_401..31_412))
|
627
|
+
.merge(ignore(31_414..31_420))
|
628
|
+
.merge(ignore(31_422..31_433))
|
629
|
+
.merge(ignore(31_435..31_446))
|
630
|
+
.merge(ignore(31_447..31_459))
|
631
|
+
.merge(ignore(31_461..31_472))
|
629
632
|
|
630
633
|
def transform_registers(registers)
|
631
634
|
registers.each do |(k, v)|
|
@@ -640,19 +643,20 @@ module Aurora
|
|
640
643
|
|
641
644
|
def print_registers(registers)
|
642
645
|
result = []
|
643
|
-
registers.each do |(k,
|
646
|
+
registers.each do |(k, value)|
|
644
647
|
# ignored
|
645
648
|
next if REGISTER_NAMES.key?(k) && REGISTER_NAMES[k].nil?
|
649
|
+
|
646
650
|
name = REGISTER_NAMES[k]
|
647
651
|
value_proc = REGISTER_CONVERTERS.find { |(_, z)| z.include?(k) }&.first || ->(v) { v }
|
648
652
|
format = REGISTER_FORMATS.find { |(_, z)| z.include?(k) }&.first || "%s"
|
649
653
|
format = "%1$d (0x%1$04x)" unless name
|
650
654
|
name ||= "???"
|
651
|
-
|
652
|
-
value = value_proc.arity == 2 ? value_proc.call(registers, k) : value_proc.call(
|
655
|
+
|
656
|
+
value = value_proc.arity == 2 ? value_proc.call(registers, k) : value_proc.call(value)
|
653
657
|
value = value.join(", ") if value.is_a?(Array)
|
654
|
-
value =
|
655
|
-
|
658
|
+
value = format(format, value) if value
|
659
|
+
|
656
660
|
result << "#{name} (#{k}): #{value}"
|
657
661
|
end
|
658
662
|
result.join("\n")
|