waterfurnace_aurora 0.3.9 → 0.3.13

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: 6920a48439c274a9c5f7cec06ebeabf6c97a91018f107d090ce8be793f307f99
4
- data.tar.gz: 4a7e518fcca1ec7c52d7bd1bee6d294a59b31e9f0d759270a1a2bcd2f8c75270
3
+ metadata.gz: a92c5ae13a08cf060e3c7f23613cc1302e20af0bc4bc7c77c610936bea48c2b5
4
+ data.tar.gz: 914c7c6371755e06524e063b5eb6820ca63f67844713d4d396cd08559fa62cc6
5
5
  SHA512:
6
- metadata.gz: 504b790c101f15dc79f5985b3fdcab529f746d912aaef0865c59404240f95908ca72a70aa262af4917b7409ac9d5366f24167eddc33ba8ba5786d0213f7b3776
7
- data.tar.gz: def8a5362616f511f514a0f3e3008302dfa0fa71d9e7ac4bd0d937cd76083ec6118cc8240e100bf5099cd610926dc912c8d4eaf50b4f7cb5149fcd5cdbbc88a8
6
+ metadata.gz: 384657c10727cf0a955cf4a4db5270d8ee021629e40c0dee65170e22206e3b0c21c85da2beade4f02143cdd5ff8d5631d5429c6a2957b96d933a3476cf83078c
7
+ data.tar.gz: 6ea98c0cd77e83158d8313a851b0fa155cc6ee7a042aa338abb27c3c57cec543017b1ab97953aedf11d4645fedd5a6423c35b2c4f75fae6c768cfd1d5e47635d
data/exe/aurora_fetch CHANGED
@@ -6,23 +6,25 @@ require "ccutrer-serialport"
6
6
  require "logger"
7
7
  require "optparse"
8
8
  require "uri"
9
+ require "yaml"
9
10
 
10
- debug_modbus = false
11
+ debug_modbus = yaml = false
11
12
 
12
- opts = OptionParser.new do |opts|
13
+ options = OptionParser.new do |opts|
13
14
  opts.banner = "Usage: aurora_fetch /path/to/serial/port REGISTERS [options]"
14
15
 
15
16
  opts.on("--debug-modbus", "Print actual protocol bytes") { debug_modbus = true }
17
+ opts.on("-y", "--yaml", "Output raw values as YAML") { yaml = true }
16
18
  opts.on("-h", "--help", "Prints this help") do
17
19
  puts opts
18
20
  exit
19
21
  end
20
22
  end
21
23
 
22
- opts.parse!
24
+ options.parse!
23
25
 
24
26
  unless ARGV.length == 2
25
- puts opts
27
+ puts options
26
28
  exit 1
27
29
  end
28
30
 
@@ -50,4 +52,8 @@ slave = client.with_slave(1)
50
52
  abc = Aurora::ABCClient.new(slave)
51
53
  registers = abc.query_registers(ARGV[1])
52
54
 
53
- puts Aurora.print_registers(registers)
55
+ if yaml
56
+ puts YAML.dump(registers)
57
+ else
58
+ puts Aurora.print_registers(registers)
59
+ end
data/exe/aurora_monitor CHANGED
@@ -77,7 +77,10 @@ server.request_callback = lambda { |uid, func, req|
77
77
  puts Aurora.print_registers(registers)
78
78
  elsif func == 16
79
79
  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 }
80
+ if ignore_awl_heartbeat && [{ 460 => 102, 461 => 0, 462 => 5 },
81
+ { 460 => 102, 461 => 0, 462 => 1 }].include?(registers)
82
+ next
83
+ end
81
84
 
82
85
  puts "#{Time.now} ===== write multiple registers to #{uid}:"
83
86
  puts Aurora.print_registers(registers)
@@ -71,6 +71,10 @@ class MQTTBridge
71
71
  property.value = @abc.public_send(property.id.tr("-", "_"))
72
72
  end
73
73
 
74
+ @abc.faults.each_with_index do |fault_count, i|
75
+ @faults["e#{i + 1}"].value = fault_count
76
+ end
77
+
74
78
  @abc.zones.each_with_index do |z, idx|
75
79
  homie_zone = @homie["zone#{idx + 1}"]
76
80
  homie_zone.each do |property|
@@ -125,6 +129,13 @@ class MQTTBridge
125
129
  end
126
130
  end
127
131
 
132
+ @faults = @homie.node("faults", "Fault History", "ABC") do |node|
133
+ @abc.faults.each_with_index do |count, i|
134
+ name = Aurora::FAULTS[i + 1]
135
+ node.property("e#{i + 1}", name || "E#{i + 1}", :integer, count)
136
+ end
137
+ end
138
+
128
139
  @abc.zones.each_with_index do |zone, i|
129
140
  type = zone.is_a?(Aurora::IZ2Zone) ? "IntelliZone 2 Zone" : "Thermostat"
130
141
  @homie.node("zone#{i + 1}", "Zone #{i + 1}", type) do |node|
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+
3
+ mkdir -p html
4
+ mkdir -p html/css
5
+ mkdir -p html/js
6
+ mkdir -p html/images
7
+
8
+ IP=${1:-172.20.10.1}
9
+
10
+ curl http://$IP/ > html/index.htm
11
+ curl http://$IP/config.htm > html/config.htm
12
+ curl http://$IP/favicon.ico > html/favicon.ico
13
+ curl http://$IP/css/index.css > html/css/index.css
14
+ curl http://$IP/css/phone.css > html/css/phone.css
15
+ curl http://$IP/js/indexc.js > html/js/indexc.js
16
+ curl http://$IP/js/configc.js > html/js/configc.js
17
+ curl http://$IP/images/aurora.png > html/images/aurora.png
18
+ curl http://$IP/images/back.png > html/images/back.png
19
+ curl http://$IP/images/cfailed.png > html/images/cfailed.png
20
+ curl http://$IP/images/cgood.png > html/images/cgood.png
21
+ curl http://$IP/images/cidle.png > html/images/cidle.png
data/exe/web_aid_tool ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "aurora"
5
+ require "ccutrer-serialport"
6
+ require "logger"
7
+ require "optparse"
8
+ require "yaml"
9
+
10
+ debug_modbus = monitor = mock = false
11
+
12
+ options = OptionParser.new do |opts|
13
+ opts.banner = "Usage: web_aid_tool /path/to/serial/port [options]"
14
+
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 }
21
+ opts.on("-h", "--help", "Prints this help") do
22
+ puts opts
23
+ exit
24
+ end
25
+ end
26
+
27
+ options.parse!
28
+
29
+ unless ARGV.length == 1
30
+ puts options
31
+ exit 1
32
+ end
33
+
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
79
+
80
+ def parse_query_string(query_string)
81
+ query_string.split("&").map { |p| p.split("=") }.to_h
82
+ end
83
+
84
+ # _don't_ do URI escaping
85
+ def encode_result(params)
86
+ params.map { |p| p.join("=") }.join("&")
87
+ end
88
+
89
+ require "sinatra"
90
+
91
+ set :public_folder, "html"
92
+
93
+ get "/" do
94
+ send_file "html/index.htm"
95
+ end
96
+
97
+ units = 0
98
+
99
+ get "/getunits.cgi" do
100
+ encode_result(units: units)
101
+ end
102
+
103
+ get "/setunits.cgi" do
104
+ units = params["units"].to_i
105
+ encode_result(error: 0)
106
+ end
107
+
108
+ get "/config.cgi" do
109
+ encode_result(
110
+ "AWL Version" => Aurora::VERSION,
111
+ "Local Web Version" => 1.08,
112
+ "SSID" => nil,
113
+ "Units" => units,
114
+ "AWL ID" => ARGV[0],
115
+ "AWL ID CRC" => nil
116
+ )
117
+ end
118
+
119
+ get "/request.cgi" do
120
+ params = parse_query_string(request.query_string)
121
+ result = params.slice("cmd", "id", "set", "addr")
122
+ result["err"] = nil
123
+
124
+ # these are just aliases to get a certain set of registers
125
+ case params["cmd"]
126
+ when "abcinfo"
127
+ params["regs"] = "2;8;88,4"
128
+ when "devices"
129
+ params["regs"] = "800;803;806,3;812;815;818;824"
130
+ end
131
+
132
+ case params["cmd"]
133
+ when "getregs", "abcinfo", "devices"
134
+ queries = params["regs"].split(";").map do |range|
135
+ start, length = range.split(",").map(&:to_i)
136
+ next start if length.nil?
137
+
138
+ start...(start + length)
139
+ end
140
+ registers = slave.read_multiple_holding_registers(*queries)
141
+ puts Aurora.print_registers(registers) if monitor
142
+ result["values"] = registers.values.join(",")
143
+ when "putregs"
144
+ writes = params["regs"].split(";").map do |write|
145
+ write.split(",").map(&:to_i)
146
+ end.compact.to_h
147
+ if monitor
148
+ puts "WRITING"
149
+ puts Aurora.print_registers(writes)
150
+ puts "==="
151
+ end
152
+
153
+ writes.each do |(addr, value)|
154
+ slave.write_holding_register(addr, value)
155
+ end
156
+ else
157
+ return ""
158
+ end
159
+
160
+ encode_result(result)
161
+ end
@@ -5,6 +5,7 @@ module Aurora
5
5
  attr_reader :modbus_slave,
6
6
  :serial_number,
7
7
  :zones,
8
+ :faults,
8
9
  :current_mode,
9
10
  :fan_speed,
10
11
  :entering_air_temperature,
@@ -34,19 +35,25 @@ module Aurora
34
35
  registers = registers_array.each_with_index.map { |r, i| [i + 105, r] }.to_h
35
36
  @serial_number = Aurora.transform_registers(registers)[105]
36
37
 
37
- @zones = if @modbus_slave.holding_registers[813].zero?
38
- [Thermostat.new(self)]
39
- else
38
+ @zones = if iz2?
40
39
  iz2_zone_count = @modbus_slave.holding_registers[483]
41
40
  (0...iz2_zone_count).map { |i| IZ2Zone.new(self, i + 1) }
41
+ else
42
+ [Thermostat.new(self)]
42
43
  end
44
+ @faults = []
43
45
  end
44
46
 
45
47
  def query_registers(query)
48
+ implicit = false
46
49
  ranges = query.split(",").map do |addr|
47
50
  case addr
48
51
  when "known"
52
+ implicit = true
49
53
  Aurora::REGISTER_NAMES.keys
54
+ when "valid"
55
+ implicit = true
56
+ break Aurora::REGISTER_RANGES
50
57
  when /^(\d+)(?:\.\.|-)(\d+)$/
51
58
  $1.to_i..$2.to_i
52
59
  else
@@ -57,13 +64,23 @@ module Aurora
57
64
  registers = {}
58
65
  queries.each do |subquery|
59
66
  registers.merge!(@modbus_slave.read_multiple_holding_registers(*subquery))
67
+ rescue ::ModBus::Errors::IllegalDataAddress
68
+ # maybe this unit doesn't respond to all the addresses we want?
69
+ raise unless implicit
70
+
71
+ # try each query individually
72
+ subquery.each do |subsubquery|
73
+ registers.merge!(@modbus_slave.read_multiple_holding_registers(subsubquery))
74
+ rescue ::ModBus::Errors::IllegalDataAddress
75
+ next
76
+ end
60
77
  end
61
78
  registers
62
79
  end
63
80
 
64
81
  def refresh
65
- registers_to_read = [19..20, 30, 340, 344, 347, 740..741, 900, 1110..1111, 1114, 1117, 1147..1153, 1165, 3027,
66
- 31_003]
82
+ registers_to_read = [6, 19..20, 25, 30, 340, 344, 347, 362, 740..741, 900, 1110..1111, 1114, 1147..1153, 1165,
83
+ 3027, 31_003]
67
84
  if zones.first.is_a?(IZ2Zone)
68
85
  zones.each_with_index do |_z, i|
69
86
  base1 = 21_203 + i * 9
@@ -74,9 +91,12 @@ module Aurora
74
91
  registers_to_read << base3
75
92
  end
76
93
  else
77
- registers_to_read << (745..747)
94
+ registers_to_read << 502
95
+ registers_to_read << (745..746)
78
96
  end
79
97
 
98
+ @faults = @modbus_slave.holding_registers[601..699]
99
+
80
100
  registers = @modbus_slave.holding_registers[*registers_to_read]
81
101
  Aurora.transform_registers(registers)
82
102
 
@@ -92,7 +112,10 @@ module Aurora
92
112
  @outdoor_temperature = registers[31_003]
93
113
  @fp1 = registers[19]
94
114
  @fp2 = registers[20]
95
- @locked_out = registers[1117]
115
+ @locked_out = registers[25] & 0x8000
116
+ @error = registers[25] & 0x7fff
117
+ @derated = (41..46).include?(@error)
118
+ @safe_mode = [47, 48, 49, 72, 74].include?(@error)
96
119
  @blower_only_ecm_speed = registers[340]
97
120
  @aux_heat_ecm_speed = registers[347]
98
121
  @compressor_watts = registers[1147]
@@ -104,16 +127,20 @@ module Aurora
104
127
  outputs = registers[30]
105
128
  @current_mode = if outputs.include?(:lockout)
106
129
  :lockout
130
+ elsif registers[362]
131
+ :dehumidify
107
132
  elsif outputs.include?(:cc2)
108
133
  outputs.include?(:rv) ? :c2 : :h2
109
134
  elsif outputs.include?(:cc)
110
135
  outputs.include?(:rv) ? :c1 : :h1
111
136
  elsif outputs.include?(:eh2)
112
- :eh2
137
+ outputs.include?(:rv) ? :eh2 : :emergency
113
138
  elsif outputs.include?(:eh1)
114
- :eh1
139
+ outputs.include?(:rv) ? :eh1 : :emergency
115
140
  elsif outputs.include?(:blower)
116
141
  :blower
142
+ elsif registers[6]
143
+ :waiting
117
144
  else
118
145
  :standby
119
146
  end
@@ -135,6 +162,85 @@ module Aurora
135
162
  @modbus_slave.holding_registers[347] = value
136
163
  end
137
164
 
165
+ def cooling_airflow_adjustment=(value)
166
+ value = 0x10000 + value if value.negative?
167
+ @modbus_slave.holding_registers[346] = value
168
+ end
169
+
170
+ def dhw_enabled=(value)
171
+ @modbus_slave.holding_registers[400] = value ? 1 : 0
172
+ end
173
+
174
+ def dhw_setpoint=(value)
175
+ @modbus_slave.holding_registers[401] = value
176
+ end
177
+
178
+ def loop_pressure_trip=(value)
179
+ @modbus_slave.holding_registers[419] = (value * 10).to_i
180
+ end
181
+
182
+ def vs_pump_control=(value)
183
+ raise ArgumentError unless (value = VS_PUMP_CONTROL.invert[value])
184
+
185
+ @modbus_slave.holding_registers[323] = value
186
+ end
187
+
188
+ def vs_pump_min=(value)
189
+ @modbus_slave.holding_registers[321] = value
190
+ end
191
+
192
+ def vs_pump_max=(value)
193
+ @modbus_slave.holding_registers[322] = value
194
+ end
195
+
196
+ def clear_fault_history
197
+ @modbus_slave.holding_registers[47] = 0x5555
198
+ end
199
+
200
+ def manual_operation(mode: :off,
201
+ compressor_speed: 0,
202
+ blower_speed: :with_compressor,
203
+ pump_speed: :with_compressor,
204
+ aux_heat: false)
205
+ raise ArgumentError, "mode must be :off, :heating, or :cooling" unless %i[off heating cooling].include?(mode)
206
+ raise ArgumentError, "compressor speed must be between 0 and 12" unless (0..12).include?(compressor_speed)
207
+
208
+ unless blower_speed == :with_compressor || (0..12).include?(blower_speed)
209
+ raise ArgumentError,
210
+ "blower speed must be :with_compressor or between 0 and 12"
211
+ end
212
+ unless pump_speed == :with_compressor || (0..100).include?(pump_speed)
213
+ raise ArgumentError,
214
+ "pump speed must be :with_compressor or between 0 and 100"
215
+ end
216
+
217
+ value = 0
218
+ value = 0x7fff if mode == :off
219
+ value |= 0x100 if mode == :cooling
220
+ value |= blower_speed == :with_compressor ? 0xf0 : (blower_speed << 4)
221
+ value |= 0x200 if aux_heat
222
+
223
+ @modbus_slave.holding_registers[3002] = value
224
+ @modbus_slave.holding_registers[323] = pump_speed == :with_compressor ? 0x7fff : pump_speed
225
+ end
226
+
227
+ # config aurora system
228
+ { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
229
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
230
+ def #{component}?
231
+ @modbus_slave.holding_registers[#{register}] != 3
232
+ end
233
+
234
+ def add_#{component}
235
+ @modbus_slave.holding_registers[#{register}] = 2
236
+ end
237
+
238
+ def remove_#{component}
239
+ @modbus_slave.holding_registers[#{register}] = 3
240
+ end
241
+ RUBY
242
+ end
243
+
138
244
  def inspect
139
245
  "#<Aurora::ABCClient #{(instance_variables - [:@modbus_slave]).map do |iv|
140
246
  "#{iv}=#{instance_variable_get(iv).inspect}"