Module: AxpertCommands

Defined in:
lib/axpert_commands.rb

Overview

A list of known commands

Commands implemented as constants Each command includes the actual command to be executed and a parser for the results of the command

Commands source: forums.aeva.asn.au/uploads/293/HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf

@author: Johan van der Vyver

Constant Summary collapse

OP =

:nodoc

::VoltronicDeviceOperation
PROTOCOL_ID =

Device protocol ID

Returns:

# An Integer specifying the protocol ID
30 # Example
OP.new(command: 'QPI', parser: lambda { |r| Integer(r.data[3..-1], 10) })
SERIAL_NUMBER =

Device serial number

Returns:

# A String specifying the device serial number
"XXXXXXXXXXXXXX" # Example
OP.new(command: 'QID', parser: lambda { |r| r.data[1..-1] })
MAIN_CPU_FIRMWARE =

Main CPU Firmware version

Returns:

# A String representing the CPU Firmware as a hexidecimal number
"124004.10" # Example
OP.new(command: 'QVFW', parser: lambda do |r|
  r.data[8..-1].split('.').map { |s| s.chars.map { |c| Integer(c, 16).to_s }.flatten.join }.join('.').upcase
end)
OTHER_CPU_FIRMWARE =

Other CPU Firmware version

Returns:

# A String representing the CPU Firmware as a hexidecimal number
"124004.10" # Example
OP.new(command: 'QVFW2', parser: lambda do |r|
  r.data[8..-1].split('.').map { |s| s.chars.map { |c| Integer(c, 16).to_s }.flatten.join }.join('.').upcase
end)
DEVICE_RATING =

Device rating information

Returns:

# A Hash containing device rating information
{ grid_voltage: 230.5, grid_current: 11.2, .. }
OP.new(command: 'QPIRI', parser: lambda do |r|
  r = r.data[1..-1].split(' ')
  { utility_voltage: Float(r[0]),
    utility_current: Float(r[1]),
    output_voltage: Float(r[2]),
    output_frequency: Float(r[3]),
    output_current: Float(r[4]),
    output_va: Integer(r[5], 10),
    output_watts: Integer(r[6], 10),
    battery_voltage: Float(r[7]),
    battery_bulk_charge_voltage: Float(r[8]),
    battery_cutoff_voltage: Float(r[9]),
    battery_bulk_charge_voltage: Float(r[10]),
    battery_float_charge_voltage: Float(r[11]),
    battery_type: ::AxpertConstants::BATTERY_TYPE[r[12]],
    maximum_utility_charge_current: Integer(r[13], 10),
    maximum_charge_current: Integer(r[14], 10),
    input_voltage_sensitivity: CONS::INPUT_VOLTAGE_SENSITIVITY[r[15]],
    output_source_priority: CONS::OUTPUT_SOURCE_PRIORITY[r[16]],
    charger_source_priority: CONS::CHARGER_SOURCE_PRIORITY[r[17]],
    maximum_parallel_units: Integer(r[18], 10),
    device_type: CONS::DEVICE_TYPE[r[19]],
    device_topology: CONS::DEVICE_TOPOLOGY[r[20]],
    output_mode: CONS::OUTPUT_MODE[r[21]],
    battery_float_charge_voltage: Float(r[22]),
    pv_parallel_ok_mode: CONS::PV_PARALLEL_OK_MODE[r[23]],
    pv_power_balance_mode: CONS::PV_POWER_BALANCE_MODE[r[24]] }
end)
DEVICE_FLAGS =

Device flags

Returns:

# A Hash containing device flag status as booleans
{enable_buzzer: true/false, nable_bypass_to_utility_on_overload: true/false, ... }
OP.new(command: 'QFLAG', parser: lambda do |r|
  r = r.data[1..-1]
  lookup = Hash.new { |_, k| raise "The device did not return the status of the flag '#{k}'" }
  r.scan(/[E][a-z]*/).first.to_s[1..-1].to_s.chars.each { |e| lookup[e.upcase] = true }
  r.scan(/[D][a-z]*/).first.to_s[1..-1].to_s.chars.each { |d| lookup[d.upcase] = false }
  { enable_buzzer: lookup['A'],
    enable_bypass_to_utility_on_overload: lookup['B'],
    enable_power_saving: lookup['J'],
    enable_lcd_timeout_escape_to_default_page: lookup['K'],
    enable_overload_restart: lookup['U'],
    enable_over_temperature_restart: lookup['V'],
    enable_lcd_backlight: lookup['X'],
    enable_primary_source_interrupt_alarm:  lookup['Y'],
    enable_fault_code_recording: lookup['Z'] }
end)
DEVICE_STATUS =

The current device status

Returns:

# A Hash containing device status information
{ grid_voltage: 230.5, grid_current: 11.2, .. }
OP.new(command: 'QPIGS', parser: lambda do |r|
  r = r.data[1..-1].split(' ')
  status = r[16].chars.map { |c| Boolean(c) }
  { utility_voltage: Float(r[0]),
    utility_frequency: Float(r[1]),
    output_voltage: Float(r[2]),
    output_frequency: Float(r[3]),
    output_va: Integer(r[4], 10),
    output_watts: Integer(r[5], 10),
    output_load_percent: Integer(r[6], 10),
    dc_bus_voltage: Integer(r[7], 10),
    battery_voltage: Float(r[8]),
    battery_charge_current: Float(r[9]),
    battery_capacity_remaining: Integer(r[10], 10),
    inverter_temperature_celsius: Integer(r[11], 10),
    pv_battery_input_current: Integer(r[12], 10),
    pv_input_voltage: Float(r[13]),
    solar_charge_controller_battery_voltage: Float(r[14]),
    battery_discharge_current: Integer(r[15], 10),
    add_sbu_priority_version: status[0],
    configuration_changed: status[1],
    solar_charge_controller_firmware_changed: status[2],
    load_on: status[3],
    battery_voltage_stable: status[4],
    charger_enabled: status[5],
    charging_from_solar_charge_controller: status[6],
    charging_from_utility: status[7] }
end)
DEVICE_MODE =

Device mode

Returns:

# A symbol denoting the device mode
See AxpertConstants::DEVICE_MODE for constants
OP.new(command: 'QMOD', parser: lambda { |r| CONS::DEVICE_MODE[r.data[1..-1].upcase] })
DEVICE_WARNING_STATUS =

Device warning status messages

Returns:

# An Array of Hashes
# Each Hash has the format { description: String description, level: :none/:fault/:warning }
# The level is only :none if a reserved error was returned which may signify an update
# of the parser is needed
[{ description: 'Bus voltage is too high', level: :fault}, ..]
OP.new(command: 'QPIWS', parser: lambda do |r|
  r = r.data[1..-1].chars.map { |s| Boolean(s) }
  parse = lambda { |desc, lvl = :none| { description: desc, level: lvl } }
  errors = []
  errors << parse.yield('Reserved') if r[0]
  errors << parse.yield('Inverter fault', :fault) if r[1]
  errors << parse.yield('Bus voltage is too high', :fault) if r[2]
  errors << parse.yield('Bus voltage is too low', :fault) if r[3]
  errors << parse.yield('Bus soft start failed ', :fault) if r[4]
  errors << parse.yield('Utility input failure', :warning) if r[5]
  errors << parse.yield('Output short circuited', :warning) if r[6]
  errors << parse.yield('Inverter voltage too low', :fault) if r[7]
  errors << parse.yield('Output voltage is too high', :fault) if r[8]
  errors << parse.yield('Over temperature', (r[1] ? :fault : :warning)) if r[9]
  errors << parse.yield('Fan is locked', (r[1] ? :fault : :warning)) if r[10]
  errors << parse.yield('Battery voltage is too high', (r[1] ? :fault : :warning)) if r[11]
  errors << parse.yield('Battery voltage is too low', 'Fault') if r[12]
  errors << parse.yield('Reserved') if r[13]
  errors << parse.yield('Battery under shutdown', :warning) if r[14]
  errors << parse.yield('Reserved') if r[15]
  errors << parse.yield('Overload', (r[1] ? :fault : :warning)) if r[16]
  errors << parse.yield('EEPROM fault', :warning) if r[17]
  errors << parse.yield('Inverter over current', :fault) if r[18]
  errors << parse.yield('Inverter soft start failed', :fault) if r[19]
  errors << parse.yield('Self test fail', :fault) if r[20]
  errors << parse.yield('Over voltage on DC output of inverter', :fault) if r[21]
  errors << parse.yield('Battery connection is open', :fault) if r[22]
  errors << parse.yield('Current sensor failed ', :fault) if r[23]
  errors << parse.yield('Battery short', :fault) if r[24]
  errors << parse.yield('Power limit', :warning) if r[25]
  errors << parse.yield('PV voltage high', :warning) if r[26]
  errors << parse.yield('MPPT overload fault', :warning) if r[27]
  errors << parse.yield('MPPT overload warning', :warning) if r[28]
  errors << parse.yield('Battery too low to charge', :warning) if r[29]
  errors << parse.yield('Reserved') if r[30]
  errors << parse.yield('Reserved') if r[31]
  errors
end)
DEFAULT_SETTINGS =

The device default settings

Returns:

# A Hash containing device defaults
{ grid_voltage: 230.5, grid_current: 11.2, .. }
OP.new(command: 'QDI', parser: lambda do |r|
  r = r.data[1..-1].split(' ')
  { output_voltage: Float(r[0]),
    output_frequency: Float(r[1]),
    maximum_ac_charge_current: Integer(r[2], 10),
    battery_cutoff_voltage: Float(r[3]),
    battery_float_charge_voltage: Float(r[4]),
    battery_bulk_charge_voltage: Float(r[5]),
    battery_bulk_charge_voltage: Float(r[6]),
    maximum_charge_current: Integer(r[7], 10),
    input_voltage_sensitivity: CONS::INPUT_VOLTAGE_SENSITIVITY[r[8]],
    output_source_priority: CONS::OUTPUT_SOURCE_PRIORITY[r[9]],
    charger_source_priority: CONS::CHARGER_SOURCE_PRIORITY[r[10]],
    battery_type: ::AxpertConstants::BATTERY_TYPE[r[11]],
    enable_buzzer: !Boolean(r[12]),
    enable_power_saving: Boolean(r[13]),
    enable_overload_restart: Boolean(r[14]),
    enable_over_temperature_restart: Boolean(r[15]),
    enable_lcd_backlight:  Boolean(r[16]),
    enable_primary_source_interrupt_alarm: Boolean(r[17]),
    enable_fault_code_recording: Boolean(r[18]),
    enable_bypass_to_utility_on_overload: Boolean(r[19]),
    enable_lcd_timeout_escape_to_default_page: Boolean(r[20]),
    output_mode: CONS::OUTPUT_MODE[r[21]],
    battery_float_charge_voltage: Float(r[22]),
    pv_parallel_ok_mode: CONS::PV_PARALLEL_OK_MODE[r[23]],
    pv_power_balance_mode: CONS::PV_POWER_BALANCE_MODE[r[24]] }
end)
ACCEPTED_CHARGE_CURRENT_VALUES =

All the possible input values for the charge current setting

Returns:

[10, 20, 30] # => Array of Integers, example given
OP.new(command: 'QMCHGCR', parser: lambda do |r|
  r.data[1..-1].split(' ').map { |s| Integer(s, 10) }
end)
ACCEPTED_UTILITY_CHARGE_CURRENT_VALUES =

All the possible input values for the utility charge current setting

Returns:

[10, 20, 30] # => Array of Integers, example given
OP.new(command: 'QMUCHGCR', parser: lambda do |r|
  r.data[1..-1].split(' ').map { |s| Integer(s, 10) }
end)
DSP_BOOTSTRAP_STATUS =

Has device undergone DSP bootstrap

Returns:

# A Boolean indicating if the device has undergone DSP bootstrap
true # Example
OP.new(command: 'QBOOT', parser: lambda { |r| Boolean(r.data[1..-1]) })
OUTPUT_MODE =

Device parallel output mode status

Returns:

# A symbol denoting the current device parallel output mode
See AxpertConstants::OUTPUT_MODE for constants
OP.new(command: 'QOPM', parser: lambda { |r| CONS::OUTPUT_MODE[r.data[2..-1]] })
PARALLEL_DEVICE_STATUS =

Parallel device information

Input: (OPTIONAL, default = 0)

# Default of 0 seems to work for single unit mode
2 # => parallel_machine_number (Parallel mode only)

Returns:

# A Hash containing parallel device status information
{ grid_voltage: 230.5, grid_current: 11.2, .. }
OP.new(command: lambda { |i = 0| "QPGS#{Integer(i)}" }, parser: lambda do |r|
  r = r.data[1..-1].split(' ')
  status = r[19].chars.to_a
  { parallel_number_exists: Boolean(r[0]),
    serial_number: r[1],
    device_mode: CONS::DEVICE_MODE[r[2]],
    fault_code: CONS::FAULT_CODE[r[3]],
    utility_voltage: Float(r[4]),
    utility_frequency: Float(r[5]),
    output_voltage: Float(r[6]),
    output_frequency: Float(r[7]),
    output_va: Integer(r[8], 10),
    output_watts: Integer(r[9], 10),
    load_percentage: Integer(r[10], 10),
    battery_voltage: Float(r[11]),
    battery_charge_current: Integer(r[12], 10),
    battery_capacity: Integer(r[13], 10),
    pv_input_voltage: Float(r[14]),
    total_charge_current: Integer(r[15], 10),
    total_output_va:  Integer(r[16], 10),
    total_output_watt:  Integer(r[17], 10),
    total_load_percentage: Integer(r[18], 10),
    solar_charge_controller_enabled: Boolean(status[0]),
    charging_from_utility: Boolean(status[1]),
    charging_from_solar_charge_controller: Boolean(status[2]),
    battery_status: [:normal, :under, :open][Integer("#{status[3]}#{status[4]}", 10)],
    line_status_ok: !Boolean(status[5]),
    load_on: Boolean(status[6]),
    configuration_changed: Boolean(status[7]),
    output_mode:  CONS::OUTPUT_MODE[r[20]],
    charger_source_priority: CONS::CHARGER_SOURCE_PRIORITY[r[21]],
    maximum_charge_current: Integer(r[22], 10),
    device_maximum_charge_current: Integer(r[23], 10),
    pv_input_current: Integer(r[24], 10),
    battery_redischarge_voltage: Integer(r[25], 10) }
end)
RESET_TO_DEFAULT =

Reset device to factory defaults

Returns:

# A Boolean indicating if the device has reset to defaults succesfully
true # Example
OP.new(command: 'PF', error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_OUTPUT_FREQUENCY =

Set device output frequency

Input:

50 # => Any integer is accepted, consult manual for valid values

Returns:

# A Boolean indicating if the frequency was set succesfully
true # Example
OP.new(command: lambda { |input| "F#{Integer(input).to_s.rjust(2, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_OUTPUT_SOURCE_PRIORITY =

Set device output source priority

Input:

# A symbol donating the output source priority setting
See AxpertConstants::OUTPUT_SOURCE_PRIORITY

Returns:

# A Boolean indicating if the output priority was set succesfully
true # Example
OP.new(command: lambda { |i| "POP#{CONS::OUTPUT_SOURCE_PRIORITY.key(i).to_s.rjust(2, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_BATTERY_RECHARGE_VOLTAGE =

Set battery re-charge voltage

Input:

24.3 # => Any float is accepted, consult manual for valid values

Returns:

# A Boolean indicating if the recharge voltage was successfully set
true # Example
OP.new(command: lambda { |input| "PBCV#{Float(input).to_s.rjust(4, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_BATTERY_REDISCHARGE_VOLTAGE =

Set battery re-discharge voltage

Input:

24.3 # => Any float is accepted, consult manual for valid values

Returns:

# A Boolean indicating if the re-discharge voltage was successfully set
true # Example
OP.new(command: lambda { |input| "PBDV#{Float(input).to_s.rjust(4, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_CHARGER_SOURCE_PRIORITY =

Set device charger priority

Input:

# (REQUIRED) Parameter 0 => A symbol donating the charger source priority
See AxpertConstants::CHARGER_SOURCE_PRIORITY

# (OPTIONAL) Parameter 1 => parallel_machine_number (Parallel mode only)
5 # => Do not pass in this value unless the device is running in parallel mode

Returns:

# A Boolean indicating if the device charger priority was set successfully
true # Example
OP.new(command: lambda do |mode, parallel_machine_number = nil|
  if parallel_machine_number.nil?
    "PPCP#{Integer(parallel_machine_number).to_s}#{CONS::CHARGER_SOURCE_PRIORITY.key(mode).to_s.rjust(2, '0')}"
  else
    "PCP#{CONS::CHARGER_SOURCE_PRIORITY.key(mode).to_s.rjust(2, '0')}"
  end
end, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_INPUT_VOLTAGE_SENSITIVITY =

Set device input voltage sensitivity

Input:

# A symbol denoting the input voltage sensitivity
See AxpertConstants::INPUT_VOLTAGE_SENSITIVITY

Returns:

# A Boolean indicating if the device input voltage sensitivity was set successfully
true # Example
OP.new(command: lambda { |i| "PGR#{CONS::INPUT_VOLTAGE_SENSITIVITY.key(i).to_s.rjust(2, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_BATTERY_TYPE =

Set battery type

Input:

# A symbol denoting the battery type
See AxpertConstants::BATTERY_TYPE

Returns:

# A Boolean indicating if the device battery type was set successfully
true # Example
OP.new(command: lambda { |i| "PBT#{CONS::BATTERY_TYPE.key(i).to_s.rjust(2, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_BATTERY_CUTOFF_VOLTAGE =

Set device battery cut-off voltage

Input:

22.1 # => Any float is accepted, consult manual for valid values

Returns:

# A Boolean indicating if the battery cut-off voltage was successfully set
true # Example
OP.new(command: lambda { |input| "PSDV#{Float(input).to_s.rjust(4, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_BATTERY_CONSTANT_CHARGING_VOLTAGE =

Set device battery constant charging voltage

Input:

28.3 # => Any float is accepted, consult manual for valid values

Returns:

# A Boolean indicating if the battery constant charging voltage was successfully set
true # Example
OP.new(command: lambda { |input| "PCVV#{Float(input).to_s.rjust(4, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_BATTERY_FLOAT_CHARGING_VOLTAGE =

Set device battery float charging voltage

Input:

26.5 # => Any float is accepted, consult manual for valid values

Returns:

# A Boolean indicating if the battery float charging was successfully set
true # Example
OP.new(command: lambda { |input| "PBFT#{Float(input).to_s.rjust(4, '0')}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_PV_PARALLEL_OK_MODE =

Set parallel PV OK condition

Input:

# A input symbol donating the PV Parallel OK condition mode setting
See AxpertConstants::PV_PARALLEL_OK_MODE

Returns:

# A Boolean indicating if the parallel PV OK condition was set successfully
true # Example
OP.new(command: lambda { |i| "PPVOKC#{CONS::PV_PARALLEL_OK_MODE.key(i).to_s}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_PV_POWER_BALANCE_MODE =

Set PV power balance mode

Input:

# A input symbol donating the PV Power balance mode
See AxpertConstants::PV_POWER_BALANCE_MODE

Returns:

# A Boolean indicating if the PV power balance mode was set successfully
true # Example
OP.new(command: lambda { |i| "PSPB#{CONS::PV_POWER_BALANCE_MODE.key(i).to_s}" }, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_MAXIMUM_CHARGING_CURRENT =

Set the maximum charging current

Input:

# (REQUIRED) Parameter 0 => Charge current 
30  # => Any integer is accepted, consult manual for valid values

# (OPTIONAL) Parameter 1 => parallel_machine_number (Parallel mode only)
5 # => Do not pass in this value unless the device is running in parallel mode

Returns:

# A Boolean indicating if the maximum charging current was set successfully
true # Example
OP.new(command: lambda do |current, parallel_machine_number = 0|
  current = Integer(current)
  if (current >= 100)
    "MNCHGC#{Integer(parallel_machine_number).to_s}#{current.to_s.rjust(3, '0')}"
  else
    "MCHGC#{Integer(parallel_machine_number).to_s}#{current.to_s.rjust(2, '0')}"
  end
end, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_MAXIMUM_UTILITY_CHARGING_CURRENT =

Set the maximum charging current for utility

Input:

# (REQUIRED) Parameter 0 => Charge current 
30  # => Any integer is accepted, consult manual for valid values

# (OPTIONAL) Parameter 1 => parallel_machine_number (Parallel mode only)
5 # => Do not pass in this value unless the device is running in parallel mode

Returns:

# A Boolean indicating if the maximum charging current for utility was set successfully
true # Example
OP.new(command: lambda do |current, parallel_machine_number = 0|
  "MUCHGC#{Integer(parallel_machine_number).to_s}#{Integer(current).to_s.rjust(2, '0')}"
end, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
SET_OUTPUT_MODE =

Set the parallel output mode (or single mode)

Input:

# (REQUIRED) Parameter 0 => A input symbol donating output mode
See AxpertConstants::OUTPUT_MODE

# (OPTIONAL) Parameter 1 => parallel_machine_number (Parallel mode only)
5 # => Do not pass in this value unless the device is running in parallel mode

Returns:

# A Boolean indicating if the parallel output mode was set successfully
true # Example
OP.new(command: lambda do |mode, parallel_machine_number = 0|
 "POPM#{CONS::OUTPUT_MODE.key(mode)}#{Integer(parallel_machine_number).to_s}"
end, error_on_nak: false, parser: lambda { |r| (r.data == '(ACK') })
CONS =

:nodoc:

::AxpertConstants