waterfurnace_aurora 0.3.11 → 0.4.1

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: fc29704fadc06f5e325457150e088018586a4f824fc776f0c8736d45f012e30d
4
- data.tar.gz: 38820a0d7713c7957d186bdbc6d3ab52cf3064fa94e15370a118058fe777d636
3
+ metadata.gz: a429ac21f24ff82252a574e3e9bcd5c1c02c5d819f236f1b2fe05d1517a9039a
4
+ data.tar.gz: ce6bd7dae57e4d2862191ce2c65e9483655aa489ee4b03824457709fea73a6ec
5
5
  SHA512:
6
- metadata.gz: f53b075711ce3df9fdd69b6daa8a1520d6830ae9c8717ede4a8de7c0379420a64e835abefeb56ec481a586f5d328c6f967ad1fca48b08933f6145d927877c8b4
7
- data.tar.gz: 5b858edd848f538c59f89a83d8372f6e3682066fac5586d7f3c350dc53a79e0bc23741fd472f3c43456467b8c51fcc6066ba092f1ba3c4a157c453f6937a1c41
6
+ metadata.gz: 5cef31050d0c211a6fce8f22a47f0a09e5bc17c09f685b45a52faccd39b4bbc40413822abcdc78e0b272b35648672457f3e2880c44d7664c2f6d88ec75fc9437
7
+ data.tar.gz: 45861b6d6a49dc88f68a9f7ca4b3d5ac15517cbe413b6f5dfd8578963df65b9fc14f6fbd44101b773137269f25a6f06acb6901f1b409067d9f99e81c8bf38af7
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
@@ -77,7 +84,10 @@ server.request_callback = lambda { |uid, func, req|
77
84
  puts Aurora.print_registers(registers)
78
85
  elsif func == 16
79
86
  registers = Range.new(req[:addr], req[:addr] + req[:quant] - 1).zip(req[:val]).to_h
80
- next if ignore_awl_heartbeat && registers == { 460 => 102, 461 => 0, 462 => 5 }
87
+ if ignore_awl_heartbeat && [{ 460 => 102, 461 => 0, 462 => 5 },
88
+ { 460 => 102, 461 => 0, 462 => 1 }].include?(registers)
89
+ next
90
+ end
81
91
 
82
92
  puts "#{Time.now} ===== write multiple registers to #{uid}:"
83
93
  puts Aurora.print_registers(registers)
@@ -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,12 @@ class MQTTBridge
71
78
  property.value = @abc.public_send(property.id.tr("-", "_"))
72
79
  end
73
80
 
81
+ if @abc.dhw?
82
+ @dhw["enabled"].value = @abc.dhw_enabled
83
+ @dhw["water-temperature"].value = @abc.dhw_water_temperature
84
+ @dhw["set-point"].value = @abc.dhw_setpoint
85
+ end
86
+
74
87
  @abc.faults.each_with_index do |fault_count, i|
75
88
  @faults["e#{i + 1}"].value = fault_count
76
89
  end
@@ -92,10 +105,10 @@ class MQTTBridge
92
105
 
93
106
  def publish_basic_attributes
94
107
  @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)
108
+ node.property("compressor-speed", "Compressor Speed", :integer, @abc.compressor_speed,
109
+ format: @abc.vs_drive? ? 0..12 : 0..2)
96
110
  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")
111
+ format: %w[lockout standby blower heating cooling eh1 eh2 emergency waiting dehumidify])
99
112
  node.property("entering-air-temperature", "Entering Air Temperature", :float, @abc.entering_air_temperature,
100
113
  unit: "ºF")
101
114
  node.property("entering-water-temperature", "Entering Water Temperature", :float,
@@ -110,7 +123,7 @@ class MQTTBridge
110
123
  end
111
124
  node.property("relative-humidity", "Relative Humidity", :integer, @abc.relative_humidity, unit: "%",
112
125
  format: 0..100)
113
- node.property("waterflow", "Waterflow", :float, unit: "gpm")
126
+ node.property("waterflow", "Waterflow", :float, @abc.waterflow, unit: "gpm")
114
127
  node.property("fp1", "FP1 Sensor", :float, @abc.fp1, unit: "ºF")
115
128
  node.property("fp2", "FP2 Sensor", :float, @abc.fp2, unit: "ºF")
116
129
  %i[compressor blower aux_heat loop_pump total].each do |component|
@@ -129,6 +142,20 @@ class MQTTBridge
129
142
  end
130
143
  end
131
144
 
145
+ if @abc.dhw?
146
+ @dhw = @homie.node("dhw", "Domestic Hot Water Generator", "DHW") do |node|
147
+ node.property("enabled", "Enabled", :boolean, @abc.dhw_enabled) do |value, property|
148
+ @mutex.synchronize { property.value = @abc.dhw_enabled = value }
149
+ end
150
+ node.property("water-temperature", "DHW Water Temperature", :float,
151
+ @abc.dhw_water_temperature, unit: "ºF")
152
+ node.property("set-point", "DHW Set Point", :float, @abc.dhw_setpoint, format: 100..140,
153
+ unit: "ºF") do |value, property|
154
+ @mutex.synchronize { property.value = @abc.dhw_setpoint = value }
155
+ end
156
+ end
157
+ end
158
+
132
159
  @faults = @homie.node("faults", "Fault History", "ABC") do |node|
133
160
  @abc.faults.each_with_index do |count, i|
134
161
  name = Aurora::FAULTS[i + 1]
@@ -191,7 +218,7 @@ end
191
218
  log_level = ARGV.include?("--debug") ? :debug : :warn
192
219
  logger = Logger.new($stdout)
193
220
  logger.level = log_level
194
- slave.logger = logger
221
+ abc.modbus_slave.logger = logger
195
222
 
196
223
  device = "aurora-#{abc.serial_number}"
197
224
  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,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "yaml"
4
+ require "uri"
5
+
3
6
  module Aurora
4
7
  class ABCClient
8
+ class << self
9
+ def open_modbus_slave(uri)
10
+ uri = URI.parse(uri)
11
+
12
+ io = case uri.scheme
13
+ when "tcp"
14
+ require "socket"
15
+ TCPSocket.new(uri.host, uri.port)
16
+ when "telnet", "rfc2217"
17
+ require "net/telnet/rfc2217"
18
+ Net::Telnet::RFC2217.new(uri.host,
19
+ port: uri.port || 23,
20
+ baud: 19_200,
21
+ parity: :even)
22
+
23
+ else
24
+ return Aurora::MockABC.new(YAML.load_file(uri.path)) if File.file?(uri.path)
25
+
26
+ require "ccutrer-serialport"
27
+ CCutrer::SerialPort.new(uri.path, baud: 19_200, parity: :even)
28
+ end
29
+
30
+ client = ::ModBus::RTUClient.new(io)
31
+ client.with_slave(1)
32
+ end
33
+ end
34
+
5
35
  attr_reader :modbus_slave,
6
36
  :serial_number,
7
37
  :zones,
8
38
  :faults,
9
39
  :current_mode,
10
40
  :fan_speed,
41
+ :dhw_enabled,
42
+ :dhw_setpoint,
11
43
  :entering_air_temperature,
12
44
  :relative_humidity,
13
45
  :leaving_air_temperature,
@@ -27,29 +59,33 @@ module Aurora
27
59
  :loop_pump_watts,
28
60
  :total_watts
29
61
 
30
- def initialize(modbus_slave)
31
- @modbus_slave = modbus_slave
62
+ def initialize(uri)
63
+ @modbus_slave = self.class.open_modbus_slave(uri)
32
64
  @modbus_slave.read_retry_timeout = 15
33
65
  @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]
66
+ registers = Aurora.transform_registers(@modbus_slave.holding_registers[88..91, 105...110, 1114])
67
+ @program = registers[88]
68
+ @serial_number = registers[105]
69
+ @dhw_water_temperature = registers[1114]
37
70
 
38
- @zones = if @modbus_slave.holding_registers[813].zero?
39
- [Thermostat.new(self)]
40
- else
71
+ @zones = if iz2?
41
72
  iz2_zone_count = @modbus_slave.holding_registers[483]
42
73
  (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
74
+ else
75
+ [Thermostat.new(self)]
43
76
  end
44
77
  @faults = []
45
78
  end
46
79
 
47
80
  def query_registers(query)
81
+ implicit = false
48
82
  ranges = query.split(",").map do |addr|
49
83
  case addr
50
84
  when "known"
85
+ implicit = true
51
86
  Aurora::REGISTER_NAMES.keys
52
87
  when "valid"
88
+ implicit = true
53
89
  break Aurora::REGISTER_RANGES
54
90
  when /^(\d+)(?:\.\.|-)(\d+)$/
55
91
  $1.to_i..$2.to_i
@@ -61,13 +97,26 @@ module Aurora
61
97
  registers = {}
62
98
  queries.each do |subquery|
63
99
  registers.merge!(@modbus_slave.read_multiple_holding_registers(*subquery))
100
+ rescue ::ModBus::Errors::IllegalDataAddress
101
+ # maybe this unit doesn't respond to all the addresses we want?
102
+ raise unless implicit
103
+
104
+ # try each query individually
105
+ subquery.each do |subsubquery|
106
+ registers.merge!(@modbus_slave.read_multiple_holding_registers(subsubquery))
107
+ rescue ::ModBus::Errors::IllegalDataAddress
108
+ next
109
+ end
64
110
  end
65
111
  registers
66
112
  end
67
113
 
68
114
  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]
115
+ registers_to_read = [6, 19..20, 25, 30, 340, 344, 347, 740..741, 900, 1110..1111, 1114, 1117, 1147..1153, 1165,
116
+ 31_003]
117
+ registers_to_read << (400..401) if dhw?
118
+ registers_to_read.concat([362, 3001]) if vs_drive?
119
+
71
120
  if zones.first.is_a?(IZ2Zone)
72
121
  zones.each_with_index do |_z, i|
73
122
  base1 = 21_203 + i * 9
@@ -78,7 +127,8 @@ module Aurora
78
127
  registers_to_read << base3
79
128
  end
80
129
  else
81
- registers_to_read << (745..747)
130
+ registers_to_read << 502
131
+ registers_to_read << (745..746)
82
132
  end
83
133
 
84
134
  @faults = @modbus_slave.holding_registers[601..699]
@@ -86,7 +136,11 @@ module Aurora
86
136
  registers = @modbus_slave.holding_registers[*registers_to_read]
87
137
  Aurora.transform_registers(registers)
88
138
 
139
+ outputs = registers[30]
140
+
89
141
  @fan_speed = registers[344]
142
+ @dhw_enabled = registers[400]
143
+ @dhw_setpoint = registers[401]
90
144
  @entering_air_temperature = registers[740]
91
145
  @relative_humidity = registers[741]
92
146
  @leaving_air_temperature = registers[900]
@@ -94,11 +148,22 @@ module Aurora
94
148
  @entering_water_temperature = registers[1111]
95
149
  @dhw_water_temperature = registers[1114]
96
150
  @waterflow = registers[1117]
97
- @compressor_speed = registers[3027]
151
+ @compressor_speed = if vs_drive?
152
+ registers[3001]
153
+ elsif outputs.include?(:cc2)
154
+ 2
155
+ elsif outputs.include?(:cc)
156
+ 1
157
+ else
158
+ 0
159
+ end
98
160
  @outdoor_temperature = registers[31_003]
99
161
  @fp1 = registers[19]
100
162
  @fp2 = registers[20]
101
- @locked_out = registers[1117]
163
+ @locked_out = registers[25] & 0x8000
164
+ @error = registers[25] & 0x7fff
165
+ @derated = (41..46).include?(@error)
166
+ @safe_mode = [47, 48, 49, 72, 74].include?(@error)
102
167
  @blower_only_ecm_speed = registers[340]
103
168
  @aux_heat_ecm_speed = registers[347]
104
169
  @compressor_watts = registers[1147]
@@ -107,19 +172,20 @@ module Aurora
107
172
  @loop_pump_watts = registers[1165]
108
173
  @total_watts = registers[1153]
109
174
 
110
- outputs = registers[30]
111
175
  @current_mode = if outputs.include?(:lockout)
112
176
  :lockout
113
- elsif outputs.include?(:cc2)
114
- outputs.include?(:rv) ? :c2 : :h2
115
- elsif outputs.include?(:cc)
116
- outputs.include?(:rv) ? :c1 : :h1
177
+ elsif registers[362]
178
+ :dehumidify
179
+ elsif outputs.include?(:cc2) || outputs.include?(:cc)
180
+ outputs.include?(:rv) ? :cooling : :heating
117
181
  elsif outputs.include?(:eh2)
118
- :eh2
182
+ outputs.include?(:rv) ? :eh2 : :emergency
119
183
  elsif outputs.include?(:eh1)
120
- :eh1
184
+ outputs.include?(:rv) ? :eh1 : :emergency
121
185
  elsif outputs.include?(:blower)
122
186
  :blower
187
+ elsif registers[6]
188
+ :waiting
123
189
  else
124
190
  :standby
125
191
  end
@@ -141,6 +207,101 @@ module Aurora
141
207
  @modbus_slave.holding_registers[347] = value
142
208
  end
143
209
 
210
+ def cooling_airflow_adjustment=(value)
211
+ value = 0x10000 + value if value.negative?
212
+ @modbus_slave.holding_registers[346] = value
213
+ end
214
+
215
+ def dhw_enabled=(value)
216
+ @modbus_slave.holding_registers[400] = value ? 1 : 0
217
+ end
218
+
219
+ def dhw_setpoint=(value)
220
+ raise ArgumentError unless (100..140).include?(value)
221
+
222
+ @modbus_slave.holding_registers[401] = (value * 10).to_i
223
+ end
224
+
225
+ def loop_pressure_trip=(value)
226
+ @modbus_slave.holding_registers[419] = (value * 10).to_i
227
+ end
228
+
229
+ def vs_pump_control=(value)
230
+ raise ArgumentError unless (value = VS_PUMP_CONTROL.invert[value])
231
+
232
+ @modbus_slave.holding_registers[323] = value
233
+ end
234
+
235
+ def vs_pump_min=(value)
236
+ @modbus_slave.holding_registers[321] = value
237
+ end
238
+
239
+ def vs_pump_max=(value)
240
+ @modbus_slave.holding_registers[322] = value
241
+ end
242
+
243
+ def line_voltage=(value)
244
+ raise ArgumentError unless (90..635).include?(value)
245
+
246
+ @modbus_slave.holding_registers[112] = value
247
+ end
248
+
249
+ def clear_fault_history
250
+ @modbus_slave.holding_registers[47] = 0x5555
251
+ end
252
+
253
+ def manual_operation(mode: :off,
254
+ compressor_speed: 0,
255
+ blower_speed: :with_compressor,
256
+ pump_speed: :with_compressor,
257
+ aux_heat: false)
258
+ raise ArgumentError, "mode must be :off, :heating, or :cooling" unless %i[off heating cooling].include?(mode)
259
+ raise ArgumentError, "compressor speed must be between 0 and 12" unless (0..12).include?(compressor_speed)
260
+
261
+ unless blower_speed == :with_compressor || (0..12).include?(blower_speed)
262
+ raise ArgumentError,
263
+ "blower speed must be :with_compressor or between 0 and 12"
264
+ end
265
+ unless pump_speed == :with_compressor || (0..100).include?(pump_speed)
266
+ raise ArgumentError,
267
+ "pump speed must be :with_compressor or between 0 and 100"
268
+ end
269
+
270
+ value = 0
271
+ value = 0x7fff if mode == :off
272
+ value |= 0x100 if mode == :cooling
273
+ value |= blower_speed == :with_compressor ? 0xf0 : (blower_speed << 4)
274
+ value |= 0x200 if aux_heat
275
+
276
+ @modbus_slave.holding_registers[3002] = value
277
+ @modbus_slave.holding_registers[323] = pump_speed == :with_compressor ? 0x7fff : pump_speed
278
+ end
279
+
280
+ def vs_drive?
281
+ @program == "ABCVSP"
282
+ end
283
+
284
+ def dhw?
285
+ (-999..999).include?(dhw_water_temperature)
286
+ end
287
+
288
+ # config aurora system
289
+ { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
290
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
291
+ def #{component}?
292
+ @modbus_slave.holding_registers[#{register}] != 3
293
+ end
294
+
295
+ def add_#{component}
296
+ @modbus_slave.holding_registers[#{register}] = 2
297
+ end
298
+
299
+ def remove_#{component}
300
+ @modbus_slave.holding_registers[#{register}] = 3
301
+ end
302
+ RUBY
303
+ end
304
+
144
305
  def inspect
145
306
  "#<Aurora::ABCClient #{(instance_variables - [:@modbus_slave]).map do |iv|
146
307
  "#{iv}=#{instance_variable_get(iv).inspect}"