waterfurnace_aurora 0.3.8 → 0.3.12

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: c36888a334377168fdfcdc0ef75481925b0249b19d62cae4c381a2dc13b1f80f
4
- data.tar.gz: 1533ce2068ba912e12110b6c094b2431fbd8e80593a06b5c9378634ef6418413
3
+ metadata.gz: fdebb583f3d84e8f1c050e21ac205d29bba5d29d91cb14de2c106a5de26917cd
4
+ data.tar.gz: 64f7a0b749c050e0041e34d6682f1428320b8bc3762532c62b825f43ff4c40b7
5
5
  SHA512:
6
- metadata.gz: 7cd4b65faa764dab63a662023710324ffe20874e11c565fab5bd5be30d4edce6d593266cc4ae688d3181f345a2d179d733d74b49524e2581bb26718dc64f2ebf
7
- data.tar.gz: 0d6e3ab96d4de61d16c4c4f8e1667b98535a2c95d1c053a836d0107c16a854182bdc259af1905ac85e08ecd88b73c03076d355032f4832a21aa9c47729ff6209
6
+ metadata.gz: 8ec3c74f737e3d38fd593831246e1153ab2ec5eef39a488aaac43c2c22bf5df92e9e613157e1b101c6af2008d451f3513b3e47a6badae0a01b966d4ed87bc3ee
7
+ data.tar.gz: 3973f340af41d8645de59291245d599a9d7993691249ad9d0b4b7b7582a11ee5d5f95d8c11493ba1be8d9f7611e755e33e6778114bb14a5197eacb9bac16f119
data/exe/aurora_fetch CHANGED
@@ -3,7 +3,30 @@
3
3
 
4
4
  require "aurora"
5
5
  require "ccutrer-serialport"
6
+ require "logger"
7
+ require "optparse"
6
8
  require "uri"
9
+ require "yaml"
10
+
11
+ debug_modbus = yaml = false
12
+
13
+ options = OptionParser.new do |opts|
14
+ opts.banner = "Usage: aurora_fetch /path/to/serial/port REGISTERS [options]"
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 }
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
7
30
 
8
31
  uri = URI.parse(ARGV[0])
9
32
 
@@ -22,8 +45,15 @@ args = case uri.scheme
22
45
  end
23
46
 
24
47
  client = ModBus::RTUClient.new(*args)
48
+ client.logger = Logger.new($stdout)
49
+ client.logger.level = debug_modbus ? :debug : :warn
50
+
25
51
  slave = client.with_slave(1)
26
52
  abc = Aurora::ABCClient.new(slave)
27
53
  registers = abc.query_registers(ARGV[1])
28
54
 
29
- 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,12 +35,13 @@ 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)
@@ -47,6 +49,8 @@ module Aurora
47
49
  case addr
48
50
  when "known"
49
51
  Aurora::REGISTER_NAMES.keys
52
+ when "valid"
53
+ break Aurora::REGISTER_RANGES
50
54
  when /^(\d+)(?:\.\.|-)(\d+)$/
51
55
  $1.to_i..$2.to_i
52
56
  else
@@ -62,8 +66,8 @@ module Aurora
62
66
  end
63
67
 
64
68
  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]
69
+ registers_to_read = [19..20, 30, 340, 344, 347, 740..741, 900, 1110..1111, 1114, 1117, 1147..1153, 1165,
70
+ 3027, 31_003]
67
71
  if zones.first.is_a?(IZ2Zone)
68
72
  zones.each_with_index do |_z, i|
69
73
  base1 = 21_203 + i * 9
@@ -74,9 +78,12 @@ module Aurora
74
78
  registers_to_read << base3
75
79
  end
76
80
  else
77
- registers_to_read << (745..747)
81
+ registers_to_read << 502
82
+ registers_to_read << (745..746)
78
83
  end
79
84
 
85
+ @faults = @modbus_slave.holding_registers[601..699]
86
+
80
87
  registers = @modbus_slave.holding_registers[*registers_to_read]
81
88
  Aurora.transform_registers(registers)
82
89
 
@@ -135,6 +142,54 @@ module Aurora
135
142
  @modbus_slave.holding_registers[347] = value
136
143
  end
137
144
 
145
+ def cooling_airflow_adjustment=(value)
146
+ value = 0x10000 + value if value.negative?
147
+ @modbus_slave.holding_registers[346] = value
148
+ end
149
+
150
+ def dhw_enabled=(value)
151
+ @modbus_slave.holding_registers[400] = value ? 1 : 0
152
+ end
153
+
154
+ def dhw_setpoint=(value)
155
+ @modbus_slave.holding_registers[401] = value
156
+ end
157
+
158
+ def loop_pressure_trip=(value)
159
+ @modbus_slave.holding_registers[419] = (value * 10).to_i
160
+ end
161
+
162
+ def vs_pump_control=(value)
163
+ raise ArgumentError unless (value = VS_PUMP_CONTROL.invert[value])
164
+
165
+ @modbus_slave.holding_registers[323] = value
166
+ end
167
+
168
+ def vs_pump_min=(value)
169
+ @modbus_slave.holding_registers[321] = value
170
+ end
171
+
172
+ def vs_pump_max=(value)
173
+ @modbus_slave.holding_registers[322] = value
174
+ end
175
+
176
+ # config aurora system
177
+ { thermostat: 800, axb: 806, iz2: 812, aoc: 815, moc: 818, eev2: 824 }.each do |(component, register)|
178
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
179
+ def #{component}?
180
+ @modbus_slave.holding_registers[#{register}] != 3
181
+ end
182
+
183
+ def add_#{component}
184
+ @modbus_slave.holding_registers[#{register}] = 2
185
+ end
186
+
187
+ def remove_#{component}
188
+ @modbus_slave.holding_registers[#{register}] = 3
189
+ end
190
+ RUBY
191
+ end
192
+
138
193
  def inspect
139
194
  "#<Aurora::ABCClient #{(instance_variables - [:@modbus_slave]).map do |iv|
140
195
  "#{iv}=#{instance_variable_get(iv).inspect}"
@@ -49,6 +49,7 @@ module Aurora
49
49
  when 0x80..0xff
50
50
  msg += read(io, 3)
51
51
  else
52
+ log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
52
53
  raise ::ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
53
54
  end
54
55
  msg
@@ -3,6 +3,11 @@
3
3
  module Aurora
4
4
  module_function
5
5
 
6
+ # take an array of ranges, and breaks it up into queryable chunks
7
+ # the ABC limits to 100 registers per read operation
8
+ # there also seem to be issues that some ranges can't be read at
9
+ # the same time as other ranges. possibly correspond to different
10
+ # components?
6
11
  def normalize_ranges(ranges)
7
12
  registers = ranges.map { |r| Array(r) }.flatten.sort.uniq
8
13
  result = []
@@ -11,7 +16,10 @@ module Aurora
11
16
  count = 0
12
17
  registers.each_with_index do |r, i|
13
18
  run_start ||= r
14
- next unless i + 1 == registers.length || r + 1 != registers[i + 1]
19
+ next unless i + 1 == registers.length ||
20
+ r + 1 != registers[i + 1] ||
21
+ (r - run_start) == 100 ||
22
+ REGISTER_BREAKPOINTS.include?(r + 1)
15
23
 
16
24
  if r == run_start
17
25
  result << r
@@ -23,7 +31,7 @@ module Aurora
23
31
  else
24
32
  range = run_start..r
25
33
  if count + range.count > 100
26
- totals << result
34
+ totals << result unless result.empty?
27
35
  result = []
28
36
  count = 0
29
37
  end
@@ -131,6 +139,18 @@ module Aurora
131
139
  }
132
140
  end
133
141
 
142
+ COMPONENT_STATUS = {
143
+ 1 => :active,
144
+ 2 => :added,
145
+ 3 => :removed
146
+ }.freeze
147
+
148
+ VS_PUMP_CONTROL = {
149
+ 0x7fff => :off,
150
+ 40 => :min,
151
+ 100 => :max
152
+ }.freeze
153
+
134
154
  SYSTEM_OUTPUTS = {
135
155
  0x01 => :cc, # compressor stage 1
136
156
  0x02 => :cc2, # compressor stage 2
@@ -320,9 +340,10 @@ module Aurora
320
340
  # intermittent off time allowed: 5, 10, 15, 20, 25, 30, 35, 40
321
341
 
322
342
  REGISTER_CONVERTERS = {
323
- TO_HUNDREDTHS => [2, 3, 807, 813, 816, 817, 819, 820, 825, 828],
343
+ TO_HUNDREDTHS => [2, 3, 801, 807, 813, 816, 817, 819, 820, 825, 828],
324
344
  method(:dipswitch_settings) => [4, 33],
325
- TO_TENTHS => [19, 20, 401, 567, 740, 745, 746, 747, 900, 1105, 1106, 1107, 1108, 1110, 1111, 1114, 1117, 1134, 1136,
345
+ TO_TENTHS => [19, 20, 401, 419, 501, 502, 567, 740, 745, 746, 747, 900,
346
+ 1105, 1106, 1107, 1108, 1110, 1111, 1114, 1117, 1134, 1136,
326
347
  12_619, 12_620,
327
348
  21_203, 21_204,
328
349
  21_212, 21_213,
@@ -344,7 +365,10 @@ module Aurora
344
365
  ->(v) { from_bitmask(v, VS_ALARM1) } => [217],
345
366
  ->(v) { from_bitmask(v, VS_ALARM2) } => [218],
346
367
  ->(v) { from_bitmask(v, VS_EEV2) } => [280],
368
+ ->(v) { VS_PUMP_CONTROL[v] } => [323],
347
369
  NEGATABLE => [346, 1146],
370
+ ->(v) { !v.zero? } => [400],
371
+ ->(v) { COMPONENT_STATUS[v] } => [800, 806, 812, 815, 818, 824, 827],
348
372
  ->(v) { from_bitmask(v, AXB_INPUTS) } => [1103],
349
373
  ->(v) { from_bitmask(v, AXB_OUTPUTS) } => [1104],
350
374
  ->(v) { TO_TENTHS.call(NEGATABLE.call(v)) } => [1136],
@@ -367,7 +391,7 @@ module Aurora
367
391
  REGISTER_FORMATS = {
368
392
  "%ds" => [1, 6, 9, 15, 84, 85],
369
393
  "%dV" => [16, 112],
370
- "%0.1fºF" => [19, 20, 401, 567, 740, 745, 746, 747, 900, 1110, 1111, 1114, 1134, 1136,
394
+ "%0.1fºF" => [19, 20, 401, 501, 502, 567, 740, 745, 746, 747, 900, 1110, 1111, 1114, 1134, 1136,
371
395
  12_619, 12_620,
372
396
  21_203, 21_204,
373
397
  21_212, 21_213,
@@ -379,6 +403,7 @@ module Aurora
379
403
  31_007, 31_010, 31_013, 31_016, 31_019, 31_022],
380
404
  "E%d" => [25, 26],
381
405
  "%d%%" => [282, 321, 322, 346, 565, 741],
406
+ "%0.1f psi" => [419],
382
407
  "%0.1fA" => [1105, 1106, 1107, 1108],
383
408
  "%0.1fgpm" => [1117],
384
409
  "%dW" => [1147, 1149, 1151, 1153, 1165],
@@ -390,7 +415,11 @@ module Aurora
390
415
  end
391
416
 
392
417
  def faults(range)
393
- range.map { |i| [i, "E#{i % 100}"] }.to_h
418
+ range.map do |i|
419
+ name = FAULTS[i % 100]
420
+ name = " (#{name})" if name
421
+ [i, "E#{i % 100}#{name}"]
422
+ end.to_h
394
423
  end
395
424
 
396
425
  def zone_registers
@@ -494,6 +523,12 @@ module Aurora
494
523
  61_000..61_009
495
524
  ].freeze
496
525
 
526
+ # see normalize_ranges
527
+ REGISTER_BREAKPOINTS = [
528
+ 12_100,
529
+ 12_500
530
+ ].freeze
531
+
497
532
  REGISTER_NAMES = {
498
533
  1 => "Random Start Delay",
499
534
  2 => "ABC Program Version",
@@ -514,6 +549,7 @@ module Aurora
514
549
  30 => "System Outputs",
515
550
  31 => "Status",
516
551
  33 => "DIP Switch Status",
552
+ 36 => "ABC Board Rev",
517
553
  50 => "ECM Speed Low (== 5)",
518
554
  51 => "ECM Speed Med (== 5)",
519
555
  52 => "ECM Speed High (== 5)",
@@ -544,6 +580,7 @@ module Aurora
544
580
  284 => "Saturated Suction Temperature", ## ?? data format
545
581
  321 => "VS Pump Min",
546
582
  322 => "VS Pump Max",
583
+ 323 => "VS Pump Control",
547
584
  340 => "Blower Only Speed",
548
585
  341 => "Lo Compressor ECM Speed",
549
586
  342 => "Hi Compressor ECM Speed",
@@ -551,9 +588,17 @@ module Aurora
551
588
  346 => "Cooling Airflow Adjustment",
552
589
  347 => "Aux Heat ECM Speed",
553
590
  362 => "Active Dehumidify", # any value is true
591
+ 460 => "IZ2??",
592
+ 461 => "IZ2??",
593
+ 462 => "IZ2 Status", # 5 when online; 1 when in setup mode
594
+ 400 => "DHW Enabled",
554
595
  401 => "DHW Setpoint",
596
+ # 403 => "DHW Status", just a guess, based on AID Tool querying this register while showing DHW settings
555
597
  414 => "On Peak/SmartGrid 2", # 0x0001 only
598
+ 419 => "Loop Pressure Trip",
556
599
  483 => "Number of IZ2 Zones",
600
+ 501 => "Set Point", # only read by AID tool? this is _not_ heating/cooling set point
601
+ 502 => "Ambient Temperature",
557
602
  564 => "IZ2 Compressor Speed Desired",
558
603
  565 => "IZ2 Blower % Desired",
559
604
  567 => "Entering Air",
@@ -561,15 +606,28 @@ module Aurora
561
606
  741 => "Relative Humidity",
562
607
  745 => "Heating Set Point",
563
608
  746 => "Cooling Set Point",
564
- 747 => "Ambient Temperature",
609
+ 747 => "Ambient Temperature", # from communicating thermostat? but set to 0 when mode is off?
610
+ 800 => "Thermostat Installed",
611
+ 801 => "Thermostat Version",
612
+ 802 => "Thermostat Revision",
613
+ 806 => "AXB Installed",
565
614
  807 => "AXB Version",
615
+ 808 => "AXB Revision",
616
+ 812 => "IZ2 Installed",
566
617
  813 => "IZ2 Version",
567
- 816 => "AOC Version 1?",
568
- 817 => "AOC Version 2?",
569
- 819 => "MOC Version 1?",
570
- 820 => "MOC Version 2?",
618
+ 814 => "IZ2 Revision",
619
+ 815 => "AOC Installed",
620
+ 816 => "AOC Version",
621
+ 817 => "AOC Revision",
622
+ 818 => "MOC Installed",
623
+ 819 => "MOC Version",
624
+ 820 => "MOC Revision",
625
+ 824 => "EEV2 Installed",
571
626
  825 => "EEV2 Version",
627
+ 826 => "EEV2 Revision",
628
+ 827 => "AWL Installed",
572
629
  828 => "AWL Version",
630
+ 829 => "AWL Revision",
573
631
  900 => "Leaving Air",
574
632
  1103 => "AXB Inputs",
575
633
  1104 => "AXB Outputs",