waterfurnace_aurora 0.3.12 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdebb583f3d84e8f1c050e21ac205d29bba5d29d91cb14de2c106a5de26917cd
4
- data.tar.gz: 64f7a0b749c050e0041e34d6682f1428320b8bc3762532c62b825f43ff4c40b7
3
+ metadata.gz: 0e43967c61aa8b5c445ec2a6024e9da5ba8cc94205cc853ff07fed0374827dc3
4
+ data.tar.gz: 875004f5c9835f25f7c18d7e7e7d632f0877db6ac7a3fc8215fa28b68c5ce2b9
5
5
  SHA512:
6
- metadata.gz: 8ec3c74f737e3d38fd593831246e1153ab2ec5eef39a488aaac43c2c22bf5df92e9e613157e1b101c6af2008d451f3513b3e47a6badae0a01b966d4ed87bc3ee
7
- data.tar.gz: 3973f340af41d8645de59291245d599a9d7993691249ad9d0b4b7b7582a11ee5d5f95d8c11493ba1be8d9f7611e755e33e6778114bb14a5197eacb9bac16f119
6
+ metadata.gz: f5d48422906f8dbac8aa83645ab3f46a9481f96d7e4720b75772dfbf2be2cf8456753f5d364a4cde3ad69fb778fd3ba29f23249ec08010806339039264bcbefc
7
+ data.tar.gz: 3a78e33d93f256e0153ae98b7f8df92da2f3138c246e1b5ef47550f4759d5502725a1d52acb6fe3f084b8a158962538ed73a3c1acf072cc151bf18ad9db2f75d
data/exe/aurora_fetch CHANGED
@@ -28,28 +28,10 @@ unless ARGV.length == 2
28
28
  exit 1
29
29
  end
30
30
 
31
- uri = URI.parse(ARGV[0])
32
-
33
- args = case uri.scheme
34
- when "tcp"
35
- require "socket"
36
- [TCPSocket.new(uri.host, uri.port)]
37
- when "telnet", "rfc2217"
38
- require "net/telnet/rfc2217"
39
- [Net::Telnet::RFC2217.new(uri.host,
40
- port: uri.port || 23,
41
- baud: 19_200,
42
- parity: :even)]
43
- else
44
- [CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)]
45
- end
46
-
47
- client = ModBus::RTUClient.new(*args)
48
- client.logger = Logger.new($stdout)
49
- client.logger.level = debug_modbus ? :debug : :warn
50
-
51
- slave = client.with_slave(1)
52
- abc = Aurora::ABCClient.new(slave)
31
+ abc = Aurora::ABCClient.new(ARGV[0])
32
+ abc.modbus_slave.logger = Logger.new($stdout)
33
+ abc.modbus_slave.logger.level = debug_modbus ? :debug : :warn
34
+
53
35
  registers = abc.query_registers(ARGV[1])
54
36
 
55
37
  if yaml
data/exe/aurora_mock CHANGED
@@ -2,29 +2,29 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "aurora"
5
- require "ccutrer-serialport"
6
5
  require "uri"
7
6
  require "yaml"
8
7
 
9
8
  uri = URI.parse(ARGV[0])
10
9
 
11
- args = case uri.scheme
12
- when "tcp"
13
- require "socket"
14
- [TCPSocket.new(uri.host, uri.port)]
15
- when "telnet", "rfc2217"
16
- require "net/telnet/rfc2217"
17
- [Net::Telnet::RFC2217.new(uri.host,
18
- port: uri.port || 23,
19
- baud: 19_200,
20
- parity: :even)]
21
- else
22
- [CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)]
23
- end
10
+ io = case uri.scheme
11
+ when "tcp"
12
+ require "socket"
13
+ TCPSocket.new(uri.host, uri.port)
14
+ when "telnet", "rfc2217"
15
+ require "net/telnet/rfc2217"
16
+ Net::Telnet::RFC2217.new(uri.host,
17
+ port: uri.port || 23,
18
+ baud: 19_200,
19
+ parity: :even)
20
+ else
21
+ require "ccutrer-serialport"
22
+ CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)
23
+ end
24
24
 
25
25
  port = ARGV[1]&.to_i || 502
26
26
 
27
- server1 = ModBus::RTUServer.new(*args)
27
+ server1 = ModBus::RTUServer.new(io)
28
28
  server1.logger = Logger.new($stdout, :debug)
29
29
  # AID Tool queries slave 1, AWL queries slave 2; just use both
30
30
  slave1 = server1.with_slave(1)
@@ -35,7 +35,7 @@ slave255 = server2.with_slave(255)
35
35
  r = slave1.holding_registers = slave2.holding_registers = slave255.holding_registers = Array.new(31_473, 0)
36
36
 
37
37
  # prepopulate some data
38
- registers = YAML.safe_load(File.read(File.expand_path("registers.yml", __dir__)))
38
+ registers = YAML.safe_load(File.read(ARGV[2]))
39
39
  registers.each { |(k, v)| r[k] = v }
40
40
 
41
41
  server1.request_callback = lambda { |uid, func, req|
data/exe/aurora_monitor CHANGED
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "aurora"
5
- require "ccutrer-serialport"
6
5
  require "logger"
7
6
  require "optparse"
8
7
  require "socket"
@@ -10,7 +9,7 @@ require "uri"
10
9
 
11
10
  diff_only = debug_modbus = ignore_awl_heartbeat = ignore_sensors = false
12
11
 
13
- OptionParser.new do |opts|
12
+ options = OptionParser.new do |opts|
14
13
  opts.banner = "Usage: aurora_monitor /path/to/serial/port [options]"
15
14
 
16
15
  opts.on("-q", "--quiet",
@@ -27,7 +26,14 @@ OptionParser.new do |opts|
27
26
  puts opts
28
27
  exit
29
28
  end
30
- end.parse!
29
+ end
30
+
31
+ options.parse!
32
+
33
+ unless ARGV.length == 2
34
+ puts options
35
+ exit 1
36
+ end
31
37
 
32
38
  uri = URI.parse(ARGV[0])
33
39
 
@@ -36,21 +42,22 @@ last_registers = {}
36
42
  SENSOR_REGISTERS = [16, 19, 20, 740, 900, 1109, 1105, 1106, 1107, 1108, 1110, 1111, 1114, 1117, 1134, 1147, 1149, 1151,
37
43
  1153, 1165].freeze
38
44
 
39
- args = case uri.scheme
40
- when "tcp"
41
- require "socket"
42
- [TCPSocket.new(uri.host, uri.port)]
43
- when "telnet", "rfc2217"
44
- require "net/telnet/rfc2217"
45
- [Net::Telnet::RFC2217.new(uri.host,
46
- port: uri.port || 23,
47
- baud: 19_200,
48
- parity: :even)]
49
- else
50
- [CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)]
51
- end
52
-
53
- server = ModBus::RTUServer.new(*args)
45
+ io = case uri.scheme
46
+ when "tcp"
47
+ require "socket"
48
+ TCPSocket.new(uri.host, uri.port)
49
+ when "telnet", "rfc2217"
50
+ require "net/telnet/rfc2217"
51
+ Net::Telnet::RFC2217.new(uri.host,
52
+ port: uri.port || 23,
53
+ baud: 19_200,
54
+ parity: :even)
55
+ else
56
+ require "ccutrer-serialport"
57
+ CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)
58
+ end
59
+
60
+ server = ModBus::RTUServer.new(io)
54
61
  server.promiscuous = true
55
62
  server.logger = Logger.new($stdout)
56
63
  server.logger.level = debug_modbus ? :debug : :warn
@@ -5,27 +5,34 @@ require "aurora"
5
5
  require "homie-mqtt"
6
6
  require "ccutrer-serialport"
7
7
  require "uri"
8
+ require "optparse"
9
+ require "yaml"
8
10
  require "aurora/core_ext/string"
9
11
 
10
- uri = URI.parse(ARGV[0])
12
+ debug_modbus = false
13
+
14
+ options = OptionParser.new do |opts|
15
+ opts.banner = "Usage: aurora_mqtt_bridge /path/to/serial/port [options]"
16
+
17
+ opts.on("--debug-modbus", "Print actual protocol bytes") { debug_modbus = true }
18
+ opts.on("-h", "--help", "Prints this help") do
19
+ puts opts
20
+ exit
21
+ end
22
+ end
23
+
24
+ options.parse!
25
+
26
+ unless ARGV.length == 2
27
+ puts options
28
+ exit 1
29
+ end
30
+
11
31
  mqtt_uri = ARGV[1]
12
32
 
13
- args = case uri.scheme
14
- when "tcp"
15
- require "socket"
16
- [TCPSocket.new(uri.host, uri.port)]
17
- when "telnet", "rfc2217"
18
- require "net/telnet/rfc2217"
19
- [Net::Telnet::RFC2217.new(uri.host,
20
- port: uri.port || 23,
21
- baud: 19_200,
22
- parity: :even)]
23
- else
24
- [CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)]
25
- end
26
-
27
- slave = ModBus::RTUClient.new(*args).with_slave(1)
28
- abc = Aurora::ABCClient.new(slave)
33
+ abc = Aurora::ABCClient.new(ARGV[0])
34
+ abc.modbus_slave.logger = Logger.new($stdout)
35
+ abc.modbus_slave.logger.level = debug_modbus ? :debug : :warn
29
36
 
30
37
  class MQTTBridge
31
38
  def initialize(abc, homie)
@@ -71,6 +78,16 @@ class MQTTBridge
71
78
  property.value = @abc.public_send(property.id.tr("-", "_"))
72
79
  end
73
80
 
81
+ @blower.each do |property|
82
+ property.value = @abc.blower.public_send(property.id.tr("-", "_"))
83
+ end
84
+
85
+ if @abc.dhw?
86
+ @dhw["enabled"].value = @abc.dhw_enabled
87
+ @dhw["water-temperature"].value = @abc.dhw_water_temperature
88
+ @dhw["set-point"].value = @abc.dhw_setpoint
89
+ end
90
+
74
91
  @abc.faults.each_with_index do |fault_count, i|
75
92
  @faults["e#{i + 1}"].value = fault_count
76
93
  end
@@ -92,15 +109,14 @@ class MQTTBridge
92
109
 
93
110
  def publish_basic_attributes
94
111
  @homie_abc = @homie.node("abc", "Aurora Basic Control", "ABC") do |node|
95
- node.property("compressor-speed", "Compressor Speed", :integer, @abc.compressor_speed, format: 0..1)
112
+ node.property("compressor-speed", "Compressor Speed", :integer, @abc.compressor_speed,
113
+ format: @abc.vs_drive? ? 0..12 : 0..2)
96
114
  node.property("current-mode", "Current Heating/Cooling Mode", :enum, @abc.current_mode,
97
- format: %w[lockout standby blower h1 h2 c1 c2 eh1 eh2])
98
- node.property("dhw-water-temperature", "DHW Water Temperature", :float, @abc.dhw_water_temperature, unit: "ºF")
115
+ format: %w[lockout standby blower heating cooling eh1 eh2 emergency waiting dehumidify])
99
116
  node.property("entering-air-temperature", "Entering Air Temperature", :float, @abc.entering_air_temperature,
100
117
  unit: "ºF")
101
118
  node.property("entering-water-temperature", "Entering Water Temperature", :float,
102
119
  @abc.entering_water_temperature, unit: "ºF")
103
- node.property("fan-speed", "Fan Speed", :integer, @abc.fan_speed, format: 0..11)
104
120
  node.property("leaving-air-temperature", "Leaving Air Temperature", :float, @abc.leaving_air_temperature,
105
121
  unit: "ºF")
106
122
  node.property("leaving-water-temperature", "Leaving Water Temperature", :float, @abc.leaving_water_temperature,
@@ -110,22 +126,49 @@ class MQTTBridge
110
126
  end
111
127
  node.property("relative-humidity", "Relative Humidity", :integer, @abc.relative_humidity, unit: "%",
112
128
  format: 0..100)
113
- node.property("waterflow", "Waterflow", :float, unit: "gpm")
129
+ node.property("waterflow", "Waterflow", :float, @abc.waterflow, unit: "gpm")
114
130
  node.property("fp1", "FP1 Sensor", :float, @abc.fp1, unit: "ºF")
115
131
  node.property("fp2", "FP2 Sensor", :float, @abc.fp2, unit: "ºF")
116
- %i[compressor blower aux_heat loop_pump total].each do |component|
132
+ %i[compressor aux_heat loop_pump total].each do |component|
117
133
  component = "#{component}_watts"
118
134
  node.property(component.tr("_", "-"), component.tr("_", " ").titleize, :integer,
119
135
  @abc.public_send(component), unit: "W")
120
136
  end
137
+ end
121
138
 
122
- node.property("blower-only-ecm-speed", "Blower Only ECM Speed", :integer, @abc.blower_only_ecm_speed,
123
- format: 1..12) do |value, property|
124
- @mutex.synchronize { property.value = @abc.blower_only_ecm_speed = value }
139
+ @blower = @homie.node("blower", "Blower", @abc.blower.type) do |node|
140
+ if @abc.blower.respond_to?(:running)
141
+ node.property("running", "Blower is running", :boolean, @abc.blower.running?)
142
+ else
143
+ node.property("speed", "Current blower speed", :integer, @abc.blower.speed, format: @abc.blower.speed_range)
144
+ end
145
+ node.property("watts", "Energy Usage", :integer, @abc.blower.watts, unit: "W") if @abc.energy_monitoring?
146
+ if @abc.blower.is_a?(Aurora::Blower::ECM)
147
+ %w[blower-only low-compressor high-compressor aux-heat].each do |setting|
148
+ field = "#{setting.tr('-', '_')}_speed"
149
+ node.property("#{setting}-speed", "#{setting.tr('-', ' ').titleize} Speed", :integer,
150
+ @abc.blower.public_send(field), format: 1..12) do |value, property|
151
+ @mutex.synchronize { property.value = @abc.blower.public_send("#{field}=", value) }
152
+ end
153
+ end
154
+ if @abc.iz2?
155
+ node.property("iz2-desired-speed", "IZ2 Desired Speed", :integer, @abc.blower.iz2_desired_speed,
156
+ format: 0..100, unit: "%")
157
+ end
125
158
  end
126
- node.property("aux-heat-ecm-speed", "Aux Heat ECM Speed", :integer, @abc.aux_heat_ecm_speed,
127
- format: 1..12) do |value, property|
128
- @mutex.synchronize { property.value = @abc.aux_heat_ecm_speed = value }
159
+ end
160
+
161
+ if @abc.dhw?
162
+ @dhw = @homie.node("dhw", "Domestic Hot Water Generator", "DHW") do |node|
163
+ node.property("enabled", "Enabled", :boolean, @abc.dhw_enabled) do |value, property|
164
+ @mutex.synchronize { property.value = @abc.dhw_enabled = value }
165
+ end
166
+ node.property("water-temperature", "DHW Water Temperature", :float,
167
+ @abc.dhw_water_temperature, unit: "ºF")
168
+ node.property("set-point", "DHW Set Point", :float, @abc.dhw_setpoint, format: 100..140,
169
+ unit: "ºF") do |value, property|
170
+ @mutex.synchronize { property.value = @abc.dhw_setpoint = value }
171
+ end
129
172
  end
130
173
  end
131
174
 
@@ -191,7 +234,7 @@ end
191
234
  log_level = ARGV.include?("--debug") ? :debug : :warn
192
235
  logger = Logger.new($stdout)
193
236
  logger.level = log_level
194
- slave.logger = logger
237
+ abc.modbus_slave.logger = logger
195
238
 
196
239
  device = "aurora-#{abc.serial_number}"
197
240
  homie = MQTT::Homie::Device.new(device, "Aurora MQTT Bridge", mqtt: mqtt_uri)
data/exe/web_aid_tool CHANGED
@@ -7,17 +7,13 @@ require "logger"
7
7
  require "optparse"
8
8
  require "yaml"
9
9
 
10
- debug_modbus = monitor = mock = false
10
+ debug_modbus = monitor = false
11
11
 
12
12
  options = OptionParser.new do |opts|
13
13
  opts.banner = "Usage: web_aid_tool /path/to/serial/port [options]"
14
14
 
15
15
  opts.on("--debug-modbus", "Print actual protocol bytes") { debug_modbus = true }
16
- opts.on("--mock",
17
- "Instead of talking to an actual heat pump, mock it with registers from the given YAML file (instead of a serial port)") do # rubocop:disable Layout/LineLength
18
- mock = true
19
- end
20
- opts.on("--monitor", "Print interperted registers as they are requested, like aurora_monitor") { monitor = true }
16
+ opts.on("--monitor", "Print interpreted registers as they are requested, like aurora_monitor") { monitor = true }
21
17
  opts.on("-h", "--help", "Prints this help") do
22
18
  puts opts
23
19
  exit
@@ -31,51 +27,9 @@ unless ARGV.length == 1
31
27
  exit 1
32
28
  end
33
29
 
34
- class MockSlave
35
- def initialize(registers)
36
- @registers = registers
37
- end
38
-
39
- def read_multiple_holding_registers(*queries)
40
- result = {}
41
- queries.each do |query|
42
- Array(query).each do |i|
43
- result[i] = @registers[i]
44
- end
45
- end
46
- result
47
- end
48
-
49
- def write_holding_register(addr, value)
50
- @registers[addr] = value
51
- end
52
- end
53
-
54
- if mock
55
- slave = MockSlave.new(YAML.load_file(ARGV[0]))
56
- else
57
- uri = URI.parse(ARGV[0])
58
-
59
- args = case uri.scheme
60
- when "tcp"
61
- require "socket"
62
- [TCPSocket.new(uri.host, uri.port)]
63
- when "telnet", "rfc2217"
64
- require "net/telnet/rfc2217"
65
- [Net::Telnet::RFC2217.new(uri.host,
66
- port: uri.port || 23,
67
- baud: 19_200,
68
- parity: :even)]
69
- else
70
- [CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)]
71
- end
72
-
73
- client = ModBus::RTUClient.new(*args)
74
- client.logger = Logger.new($stdout)
75
- client.logger.level = debug_modbus ? :debug : :warn
76
-
77
- slave = client.with_slave(1)
78
- end
30
+ slave = Aurora::ABCClient.open_modbus_slave(ARGV[0])
31
+ slave.logger = Logger.new($stdout)
32
+ slave.logger.level = debug_modbus ? :debug : :warn
79
33
 
80
34
  def parse_query_string(query_string)
81
35
  query_string.split("&").map { |p| p.split("=") }.to_h
@@ -89,6 +43,7 @@ end
89
43
  require "sinatra"
90
44
 
91
45
  set :public_folder, "html"
46
+ set :logging, false
92
47
 
93
48
  get "/" do
94
49
  send_file "html/index.htm"
@@ -138,7 +93,10 @@ get "/request.cgi" do
138
93
  start...(start + length)
139
94
  end
140
95
  registers = slave.read_multiple_holding_registers(*queries)
141
- puts Aurora.print_registers(registers) if monitor
96
+ if monitor
97
+ puts "READING"
98
+ puts Aurora.print_registers(registers)
99
+ end
142
100
  result["values"] = registers.values.join(",")
143
101
  when "putregs"
144
102
  writes = params["regs"].split(";").map do |write|
@@ -147,7 +105,6 @@ get "/request.cgi" do
147
105
  if monitor
148
106
  puts "WRITING"
149
107
  puts Aurora.print_registers(writes)
150
- puts "==="
151
108
  end
152
109
 
153
110
  writes.each do |(addr, value)|
@@ -1,13 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "yaml"
4
+ require "uri"
5
+
6
+ require "aurora/blower"
7
+ require "aurora/iz2_zone"
8
+ require "aurora/thermostat"
9
+
3
10
  module Aurora
4
11
  class ABCClient
12
+ class << self
13
+ def open_modbus_slave(uri)
14
+ uri = URI.parse(uri)
15
+
16
+ io = case uri.scheme
17
+ when "tcp"
18
+ require "socket"
19
+ TCPSocket.new(uri.host, uri.port)
20
+ when "telnet", "rfc2217"
21
+ require "net/telnet/rfc2217"
22
+ Net::Telnet::RFC2217.new(uri.host,
23
+ port: uri.port || 23,
24
+ baud: 19_200,
25
+ parity: :even)
26
+
27
+ else
28
+ return Aurora::MockABC.new(YAML.load_file(uri.path)) if File.file?(uri.path)
29
+
30
+ require "ccutrer-serialport"
31
+ CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)
32
+ end
33
+
34
+ client = ::ModBus::RTUClient.new(io)
35
+ client.with_slave(1)
36
+ end
37
+ end
38
+
5
39
  attr_reader :modbus_slave,
6
40
  :serial_number,
7
41
  :zones,
42
+ :blower,
8
43
  :faults,
9
44
  :current_mode,
10
- :fan_speed,
45
+ :dhw_enabled,
46
+ :dhw_setpoint,
11
47
  :entering_air_temperature,
12
48
  :relative_humidity,
13
49
  :leaving_air_temperature,
@@ -19,21 +55,27 @@ module Aurora
19
55
  :outdoor_temperature,
20
56
  :fp1,
21
57
  :fp2,
22
- :blower_only_ecm_speed,
23
- :aux_heat_ecm_speed,
24
58
  :compressor_watts,
25
- :blower_watts,
26
59
  :aux_heat_watts,
27
60
  :loop_pump_watts,
28
61
  :total_watts
29
62
 
30
- def initialize(modbus_slave)
31
- @modbus_slave = modbus_slave
63
+ def initialize(uri)
64
+ @modbus_slave = self.class.open_modbus_slave(uri)
32
65
  @modbus_slave.read_retry_timeout = 15
33
66
  @modbus_slave.read_retries = 2
34
- registers_array = @modbus_slave.holding_registers[105...110]
35
- registers = registers_array.each_with_index.map { |r, i| [i + 105, r] }.to_h
36
- @serial_number = Aurora.transform_registers(registers)[105]
67
+ raw_registers = @modbus_slave.holding_registers[88..91, 105...110, 404, 412, 1114]
68
+ registers = Aurora.transform_registers(raw_registers.dup)
69
+ @program = registers[88]
70
+ @serial_number = registers[105]
71
+ @dhw_water_temperature = registers[1114]
72
+ @energy_monitor = raw_registers[412]
73
+
74
+ @blower = case raw_registers[404]
75
+ when 1, 2 then Blower::ECM.new(self, registers[404])
76
+ when 3 then Blower::FiveSpeed.new(self, registers[404])
77
+ else; Blower::PSC.new(self, registers[404])
78
+ end
37
79
 
38
80
  @zones = if iz2?
39
81
  iz2_zone_count = @modbus_slave.holding_registers[483]
@@ -45,11 +87,14 @@ module Aurora
45
87
  end
46
88
 
47
89
  def query_registers(query)
90
+ implicit = false
48
91
  ranges = query.split(",").map do |addr|
49
92
  case addr
50
93
  when "known"
94
+ implicit = true
51
95
  Aurora::REGISTER_NAMES.keys
52
96
  when "valid"
97
+ implicit = true
53
98
  break Aurora::REGISTER_RANGES
54
99
  when /^(\d+)(?:\.\.|-)(\d+)$/
55
100
  $1.to_i..$2.to_i
@@ -61,13 +106,27 @@ module Aurora
61
106
  registers = {}
62
107
  queries.each do |subquery|
63
108
  registers.merge!(@modbus_slave.read_multiple_holding_registers(*subquery))
109
+ rescue ::ModBus::Errors::IllegalDataAddress
110
+ # maybe this unit doesn't respond to all the addresses we want?
111
+ raise unless implicit
112
+
113
+ # try each query individually
114
+ subquery.each do |subsubquery|
115
+ registers.merge!(@modbus_slave.read_multiple_holding_registers(subsubquery))
116
+ rescue ::ModBus::Errors::IllegalDataAddress
117
+ next
118
+ end
64
119
  end
65
120
  registers
66
121
  end
67
122
 
68
123
  def refresh
69
- registers_to_read = [19..20, 30, 340, 344, 347, 740..741, 900, 1110..1111, 1114, 1117, 1147..1153, 1165,
70
- 3027, 31_003]
124
+ registers_to_read = [6, 19..20, 25, 30, 344, 740..741, 900, 1110..1111, 1114, 1117, 1147..1153, 1165,
125
+ 31_003]
126
+ registers_to_read << (400..401) if dhw?
127
+ registers_to_read.concat(blower.registers_to_read)
128
+ registers_to_read.concat([362, 3001]) if vs_drive?
129
+
71
130
  if zones.first.is_a?(IZ2Zone)
72
131
  zones.each_with_index do |_z, i|
73
132
  base1 = 21_203 + i * 9
@@ -87,7 +146,10 @@ module Aurora
87
146
  registers = @modbus_slave.holding_registers[*registers_to_read]
88
147
  Aurora.transform_registers(registers)
89
148
 
90
- @fan_speed = registers[344]
149
+ outputs = registers[30]
150
+
151
+ @dhw_enabled = registers[400]
152
+ @dhw_setpoint = registers[401]
91
153
  @entering_air_temperature = registers[740]
92
154
  @relative_humidity = registers[741]
93
155
  @leaving_air_temperature = registers[900]
@@ -95,36 +157,47 @@ module Aurora
95
157
  @entering_water_temperature = registers[1111]
96
158
  @dhw_water_temperature = registers[1114]
97
159
  @waterflow = registers[1117]
98
- @compressor_speed = registers[3027]
160
+ @compressor_speed = if vs_drive?
161
+ registers[3001]
162
+ elsif outputs.include?(:cc2)
163
+ 2
164
+ elsif outputs.include?(:cc)
165
+ 1
166
+ else
167
+ 0
168
+ end
99
169
  @outdoor_temperature = registers[31_003]
100
170
  @fp1 = registers[19]
101
171
  @fp2 = registers[20]
102
- @locked_out = registers[1117]
103
- @blower_only_ecm_speed = registers[340]
104
- @aux_heat_ecm_speed = registers[347]
172
+ @locked_out = registers[25] & 0x8000
173
+ @error = registers[25] & 0x7fff
174
+ @derated = (41..46).include?(@error)
175
+ @safe_mode = [47, 48, 49, 72, 74].include?(@error)
105
176
  @compressor_watts = registers[1147]
106
- @blower_watts = registers[1149]
107
177
  @aux_heat_watts = registers[1151]
108
178
  @loop_pump_watts = registers[1165]
109
179
  @total_watts = registers[1153]
110
180
 
111
- outputs = registers[30]
112
181
  @current_mode = if outputs.include?(:lockout)
113
182
  :lockout
114
- elsif outputs.include?(:cc2)
115
- outputs.include?(:rv) ? :c2 : :h2
116
- elsif outputs.include?(:cc)
117
- outputs.include?(:rv) ? :c1 : :h1
183
+ elsif registers[362]
184
+ :dehumidify
185
+ elsif outputs.include?(:cc2) || outputs.include?(:cc)
186
+ outputs.include?(:rv) ? :cooling : :heating
118
187
  elsif outputs.include?(:eh2)
119
- :eh2
188
+ outputs.include?(:rv) ? :eh2 : :emergency
120
189
  elsif outputs.include?(:eh1)
121
- :eh1
190
+ outputs.include?(:rv) ? :eh1 : :emergency
122
191
  elsif outputs.include?(:blower)
123
192
  :blower
193
+ elsif registers[6]
194
+ :waiting
124
195
  else
125
196
  :standby
126
197
  end
127
198
 
199
+ blower.refresh(registers)
200
+
128
201
  zones.each do |z|
129
202
  z.refresh(registers)
130
203
  end
@@ -152,7 +225,9 @@ module Aurora
152
225
  end
153
226
 
154
227
  def dhw_setpoint=(value)
155
- @modbus_slave.holding_registers[401] = value
228
+ raise ArgumentError unless (100..140).include?(value)
229
+
230
+ @modbus_slave.holding_registers[401] = (value * 10).to_i
156
231
  end
157
232
 
158
233
  def loop_pressure_trip=(value)
@@ -173,11 +248,61 @@ module Aurora
173
248
  @modbus_slave.holding_registers[322] = value
174
249
  end
175
250
 
251
+ def line_voltage=(value)
252
+ raise ArgumentError unless (90..635).include?(value)
253
+
254
+ @modbus_slave.holding_registers[112] = value
255
+ end
256
+
257
+ def clear_fault_history
258
+ @modbus_slave.holding_registers[47] = 0x5555
259
+ end
260
+
261
+ def manual_operation(mode: :off,
262
+ compressor_speed: 0,
263
+ blower_speed: :with_compressor,
264
+ pump_speed: :with_compressor,
265
+ aux_heat: false)
266
+ raise ArgumentError, "mode must be :off, :heating, or :cooling" unless %i[off heating cooling].include?(mode)
267
+ raise ArgumentError, "compressor speed must be between 0 and 12" unless (0..12).include?(compressor_speed)
268
+
269
+ unless blower_speed == :with_compressor || (0..12).include?(blower_speed)
270
+ raise ArgumentError,
271
+ "blower speed must be :with_compressor or between 0 and 12"
272
+ end
273
+ unless pump_speed == :with_compressor || (0..100).include?(pump_speed)
274
+ raise ArgumentError,
275
+ "pump speed must be :with_compressor or between 0 and 100"
276
+ end
277
+
278
+ value = 0
279
+ value = 0x7fff if mode == :off
280
+ value |= 0x100 if mode == :cooling
281
+ value |= blower_speed == :with_compressor ? 0xf0 : (blower_speed << 4)
282
+ value |= 0x200 if aux_heat
283
+
284
+ @modbus_slave.holding_registers[3002] = value
285
+ @modbus_slave.holding_registers[323] = pump_speed == :with_compressor ? 0x7fff : pump_speed
286
+ end
287
+
288
+ def energy_monitoring?
289
+ @energy_monitor == 2
290
+ end
291
+
292
+ def vs_drive?
293
+ @program == "ABCVSP"
294
+ end
295
+
296
+ def dhw?
297
+ (-999..999).include?(dhw_water_temperature)
298
+ end
299
+
176
300
  # config aurora system
177
301
  { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
178
302
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
179
303
  def #{component}?
180
- @modbus_slave.holding_registers[#{register}] != 3
304
+ return @#{component} if instance_variable_defined?(:@#{component})
305
+ @#{component} = @modbus_slave.holding_registers[#{register}] != 3
181
306
  end
182
307
 
183
308
  def add_#{component}