node-red-contrib-victron-vedirect-usb
Version:
Access the Victron equipment with a USB VE.Direct cable
659 lines (645 loc) • 18.4 kB
JavaScript
const { Transform } = require('stream')
const checksum = (blockBuffer) => {
return blockBuffer.reduce((prev, curr) => {
return (prev + curr) & 255
}, 0)
}
const products = {
'0x203': 'BMV-700',
'0x204': 'BMV-702',
'0x205': 'BMV-700H',
'0x0300': 'BlueSolar MPPT 70|15',
'0xA040': 'BlueSolar MPPT 75|50',
'0xA041': 'BlueSolar MPPT 150|35',
'0xA042': 'BlueSolar MPPT 75|15',
'0xA043': 'BlueSolar MPPT 100|15',
'0xA044': 'BlueSolar MPPT 100|30',
'0xA045': 'BlueSolar MPPT 100|50',
'0xA046': 'BlueSolar MPPT 150|70',
'0xA047': 'BlueSolar MPPT 150|100',
'0xA049': 'BlueSolar MPPT 100|50 rev2',
'0xA04A': 'BlueSolar MPPT 100|30 rev2',
'0xA04B': 'BlueSolar MPPT 150|35 rev2',
'0xA04C': 'BlueSolar MPPT 75|10',
'0xA04D': 'BlueSolar MPPT 150|45',
'0xA04E': 'BlueSolar MPPT 150|60',
'0xA04F': 'BlueSolar MPPT 150|85',
'0xA050': 'SmartSolar MPPT 250|100',
'0xA051': 'SmartSolar MPPT 150|100',
'0xA052': 'SmartSolar MPPT 150|85',
'0xA053': 'SmartSolar MPPT 75|15',
'0xA054': 'SmartSolar MPPT 75|10',
'0xA055': 'SmartSolar MPPT 100|15',
'0xA056': 'SmartSolar MPPT 100|30',
'0xA057': 'SmartSolar MPPT 100|50',
'0xA058': 'SmartSolar MPPT 150|35',
'0xA059': 'SmartSolar MPPT 150|100 rev2',
'0xA05A': 'SmartSolar MPPT 150|85 rev2',
'0xA05B': 'SmartSolar MPPT 250|70',
'0xA05C': 'SmartSolar MPPT 250|85',
'0xA05D': 'SmartSolar MPPT 250|60',
'0xA05E': 'SmartSolar MPPT 250|45',
'0xA05F': 'SmartSolar MPPT 100|20',
'0xA060': 'SmartSolar MPPT 100|20 48V',
'0xA061': 'SmartSolar MPPT 150|45',
'0xA062': 'SmartSolar MPPT 150|60',
'0xA063': 'SmartSolar MPPT 150|70',
'0xA064': 'SmartSolar MPPT 250|85 rev2',
'0xA065': 'SmartSolar MPPT 250|100 rev2',
'0xA066': 'BlueSolar MPPT 100|20',
'0xA067': 'BlueSolar MPPT 100|20 48V',
'0xA068': 'SmartSolar MPPT 250|60 rev2',
'0xA069': 'SmartSolar MPPT 250|70 rev2',
'0xA06A': 'SmartSolar MPPT 150|45 rev2',
'0xA06B': 'SmartSolar MPPT 150|60 rev2',
'0xA06C': 'SmartSolar MPPT 150|70 rev2',
'0xA06D': 'SmartSolar MPPT 150|85 rev3',
'0xA06E': 'SmartSolar MPPT 150|100 rev3',
'0xA06F': 'BlueSolar MPPT 150|45 rev2',
'0xA070': 'BlueSolar MPPT 150|60 rev2',
'0xA071': 'BlueSolar MPPT 150|70 rev2',
'0xA072': 'BlueSolar MPPT 150/45 rev3',
'0xA073': 'SmartSolar MPPT 150/45 rev3',
'0xA074': 'SmartSolar MPPT 75/10 rev2',
'0xA075': 'SmartSolar MPPT 75/15 rev2',
'0xA076': 'BlueSolar MPPT 100/30 rev3',
'0xA077': 'BlueSolar MPPT 100/50 rev3',
'0xA078': 'BlueSolar MPPT 150/35 rev3',
'0xA079': 'BlueSolar MPPT 75/10 rev2',
'0xA07A': 'BlueSolar MPPT 75/15 rev2',
'0xA07B': 'BlueSolar MPPT 100/15 rev2',
'0xA07C': 'BlueSolar MPPT 75/10 rev3',
'0xA07D': 'BlueSolar MPPT 75/15 rev3',
'0xA07E': 'SmartSolar MPPT 100/30 12V',
'0xA07F': 'All-In-1 SmartSolar MPPT 75/15 12V',
'0xA102': 'SmartSolar MPPT VE.Can 150/70',
'0xA103': 'SmartSolar MPPT VE.Can 150/45',
'0xA104': 'SmartSolar MPPT VE.Can 150/60',
'0xA105': 'SmartSolar MPPT VE.Can 150/85',
'0xA106': 'SmartSolar MPPT VE.Can 150/100',
'0xA107': 'SmartSolar MPPT VE.Can 250/45',
'0xA108': 'SmartSolar MPPT VE.Can 250/60',
'0xA109': 'SmartSolar MPPT VE.Can 250/70',
'0xA10A': 'SmartSolar MPPT VE.Can 250/85',
'0xA10B': 'SmartSolar MPPT VE.Can 250/100',
'0xA10C': 'SmartSolar MPPT VE.Can 150/70 rev2',
'0xA10D': 'SmartSolar MPPT VE.Can 150/85 rev2',
'0xA10E': 'SmartSolar MPPT VE.Can 150/100 rev2',
'0xA10F': 'BlueSolar MPPT VE.Can 150/100',
'0xA112': 'BlueSolar MPPT VE.Can 250/70',
'0xA113': 'BlueSolar MPPT VE.Can 250/100',
'0xA114': 'SmartSolar MPPT VE.Can 250/70 rev2',
'0xA115': 'SmartSolar MPPT VE.Can 250/100 rev2',
'0xA116': 'SmartSolar MPPT VE.Can 250/85 rev2',
'0xA201': 'Phoenix Inverter 12V 250VA 230V',
'0xA202': 'Phoenix Inverter 24V 250VA 230V',
'0xA204': 'Phoenix Inverter 48V 250VA 230V',
'0xA211': 'Phoenix Inverter 12V 375VA 230V',
'0xA212': 'Phoenix Inverter 24V 375VA 230V',
'0xA214': 'Phoenix Inverter 48V 375VA 230V',
'0xA221': 'Phoenix Inverter 12V 500VA 230V',
'0xA222': 'Phoenix Inverter 24V 500VA 230V',
'0xA224': 'Phoenix Inverter 48V 500VA 230V',
'0xA231': 'Phoenix Inverter 12V 250VA 230V',
'0xA232': 'Phoenix Inverter 24V 250VA 230V',
'0xA234': 'Phoenix Inverter 48V 250VA 230V',
'0xA239': 'Phoenix Inverter 12V 250VA 120V',
'0xA23A': 'Phoenix Inverter 24V 250VA 120V',
'0xA23C': 'Phoenix Inverter 48V 250VA 120V',
'0xA241': 'Phoenix Inverter 12V 375VA 230V',
'0xA242': 'Phoenix Inverter 24V 375VA 230V',
'0xA244': 'Phoenix Inverter 48V 375VA 230V',
'0xA249': 'Phoenix Inverter 12V 375VA 120V',
'0xA24A': 'Phoenix Inverter 24V 375VA 120V',
'0xA24C': 'Phoenix Inverter 48V 375VA 120V',
'0xA251': 'Phoenix Inverter 12V 500VA 230V',
'0xA252': 'Phoenix Inverter 24V 500VA 230V',
'0xA254': 'Phoenix Inverter 48V 500VA 230V',
'0xA259': 'Phoenix Inverter 12V 500VA 120V',
'0xA25A': 'Phoenix Inverter 24V 500VA 120V',
'0xA25C': 'Phoenix Inverter 48V 500VA 120V',
'0xA261': 'Phoenix Inverter 12V 800VA 230V',
'0xA262': 'Phoenix Inverter 24V 800VA 230V',
'0xA264': 'Phoenix Inverter 48V 800VA 230V',
'0xA269': 'Phoenix Inverter 12V 800VA 120V',
'0xA26A': 'Phoenix Inverter 24V 800VA 120V',
'0xA26C': 'Phoenix Inverter 48V 800VA 120V',
'0xA271': 'Phoenix Inverter 12V 1200VA 230V',
'0xA272': 'Phoenix Inverter 24V 1200VA 230V',
'0xA274': 'Phoenix Inverter 48V 1200VA 230V',
'0xA279': 'Phoenix Inverter 12V 1200VA 120V',
'0xA27A': 'Phoenix Inverter 24V 1200VA 120V',
'0xA27C': 'Phoenix Inverter 48V 1200VA 120V',
'0xA281': 'Phoenix Inverter 12V 1600VA 230V',
'0xA282': 'Phoenix Inverter 24V 1600VA 230V',
'0xA284': 'Phoenix Inverter 48V 1600VA 230V',
'0xA291': 'Phoenix Inverter 12V 2000VA 230V',
'0xA292': 'Phoenix Inverter 24V 2000VA 230V',
'0xA294': 'Phoenix Inverter 48V 2000VA 230V',
'0xA2A1': 'Phoenix Inverter 12V 3000VA 230V',
'0xA2A2': 'Phoenix Inverter 24V 3000VA 230V',
'0xA2A4': 'Phoenix Inverter 48V 3000VA 230V',
'0xA340': 'Phoenix Smart IP43 Charger 12|50 (1+1)',
'0xA341': 'Phoenix Smart IP43 Charger 12|50 (3)',
'0xA342': 'Phoenix Smart IP43 Charger 24|25 (1+1)',
'0xA343': 'Phoenix Smart IP43 Charger 24|25 (3)',
'0xA344': 'Phoenix Smart IP43 Charger 12|30 (1+1)',
'0xA345': 'Phoenix Smart IP43 Charger 12|30 (3)',
'0xA346': 'Phoenix Smart IP43 Charger 24|16 (1+1)',
'0xA347': 'Phoenix Smart IP43 Charger 24|16 (3)',
'0xA381': 'BMV-712 Smart',
'0xA382': 'BMV-710H Smart',
'0xA383': 'BMV-712 Smart Rev2',
'0xA389': 'SmartShunt 500A/50mV',
'0xA38A': 'SmartShunt 1000A/50mV',
'0xA38B': 'SmartShunt 2000A/50mV',
'0xA3F0': 'Smart BuckBoost 12V/12V-50A'
}
const parseValues = (frame) => {
for (const key in frame) {
switch (key) {
case 'V':
frame[key] = {
value: frame[key],
description: 'Main or channel 1 (battery) voltage',
units: 'mV'
}
break
case 'V2':
frame[key] = {
value: frame[key],
description: 'Channel 2 (battery) voltage',
units: 'mV'
}
break
case 'V3':
frame[key] = {
value: frame[key],
description: 'Channel 3 (battery) voltage',
units: 'mV'
}
break
case 'VS':
frame[key] = {
value: frame[key],
description: 'Auxiliary (starter) voltage',
units: 'mV'
}
break
case 'VM':
frame[key] = {
value: frame[key],
description: 'Mid-point voltage of the battery bank',
units: 'mV'
}
break
case 'DM':
frame[key] = {
value: frame[key],
description: 'Mid-point deviation of the battery bank',
units: '‰'
}
break
case 'VPV':
frame[key] = {
value: frame[key],
description: 'Panel voltage',
units: 'mV'
}
break
case 'PPV':
frame[key] = {
value: frame[key],
description: 'Panel power',
units: 'W'
}
break
case 'I':
frame[key] = {
value: frame[key],
description: 'Main or channel 1 battery current',
units: 'mA'
}
break
case 'I2':
frame[key] = {
value: frame[key],
description: 'Channel 2 battery current',
units: 'mA'
}
break
case 'I3':
frame[key] = {
value: frame[key],
description: 'Channel 3 battery current',
units: 'mA'
}
break
case 'IL':
frame[key] = {
value: frame[key],
description: 'Load current',
units: 'mA'
}
break
case 'LOAD':
frame[key] = {
value: frame[key],
description: 'Load output state (ON/OFF)',
units: 'W'
}
break
case 'T':
frame[key] = {
value: frame[key],
description: 'Battery temperature',
units: '°C'
}
break
case 'P':
frame[key] = {
value: frame[key],
description: 'Instantaneous power',
units: 'W'
}
break
case 'CE':
frame[key] = {
value: frame[key],
description: 'Consumed Amp Hours',
units: 'mAh'
}
break
case 'SOC':
frame[key] = {
value: frame[key],
description: 'State-of-charge',
units: '‰'
}
break
case 'TTG':
frame[key] = {
value: frame[key],
description: 'Time-to-go',
units: 'Minutes'
}
break
case 'Alarm':
frame[key] = {
value: frame[key],
description: 'Alarm condition active',
units: ''
}
break
case 'Relay':
frame[key] = {
value: frame[key],
description: 'Relay state',
units: ''
}
break
case 'AR':
frame[key] = {
value: frame[key],
description: 'Alarm reason',
units: ''
}
break
case 'OR':
frame[key] = {
value: frame[key],
description: 'Off reason',
units: ''
}
break
case 'H1':
frame[key] = {
value: frame[key],
description: 'Depth of the deepest discharge',
units: 'mAh'
}
break
case 'H2':
frame[key] = {
value: frame[key],
description: 'Depth of the last discharge',
units: 'mAh'
}
break
case 'H3':
frame[key] = {
value: frame[key],
description: 'Depth of the average discharge',
units: 'mAh'
}
break
case 'H4':
frame[key] = {
value: frame[key],
description: 'Number of charge cycles',
units: ''
}
break
case 'H5':
frame[key] = {
value: frame[key],
description: 'Number of full discharges',
units: ''
}
break
case 'H6':
frame[key] = {
value: frame[key],
description: 'Cumulative Amp Hours drawn',
units: 'mAh'
}
break
case 'H7':
frame[key] = {
value: frame[key],
description: 'Minimum main (battery) voltage',
units: 'mV'
}
break
case 'H8':
frame[key] = {
value: frame[key],
description: 'Maximum main (battery) voltage',
units: 'mV'
}
break
case 'H9':
frame[key] = {
value: frame[key],
description: 'Number of seconds since last full charge',
units: 'Seconds'
}
break
case 'H10':
frame[key] = {
value: frame[key],
description: 'Number of automatic synchronizations',
units: ''
}
break
case 'H11':
frame[key] = {
value: frame[key],
description: 'Number of low main voltage alarms',
units: ''
}
break
case 'H12':
frame[key] = {
value: frame[key],
description: 'Number of high main voltage alarms',
units: ''
}
break
case 'H13':
frame[key] = {
value: frame[key],
description: 'Number of low auxiliary voltage alarms',
units: ''
}
break
case 'H14':
frame[key] = {
value: frame[key],
description: 'Number of high auxiliary voltage alarms',
units: ''
}
break
case 'H15':
frame[key] = {
value: frame[key],
description: 'Minimum auxiliary (battery) voltage',
units: 'mV'
}
break
case 'H16':
frame[key] = {
value: frame[key],
description: 'Maximum auxiliary (battery) voltage',
units: 'mV'
}
break
case 'H17':
frame[key] = {
value: frame[key],
description: 'Amount of discharged energy (BMV) / Amount of produced energy (DC monitor)',
units: '0.01 kWh'
}
break
case 'H18':
frame[key] = {
value: frame[key],
description: 'Amount of charged energy (BMV) / Amount of consumed energy (DC monitor)',
units: '0.01 kWh'
}
break
case 'H19':
frame[key] = {
value: frame[key],
description: 'Yield total (user resettable counter)',
units: '0.01 kWh'
}
break
case 'H20':
frame[key] = {
value: frame[key],
description: 'Yield today',
units: '0.01 kWh'
}
break
case 'H21':
frame[key] = {
value: frame[key],
description: 'Maximum power today',
units: 'W'
}
break
case 'H22':
frame[key] = {
value: frame[key],
description: 'Yield yesterday',
units: '0.01 kWh'
}
break
case 'H23':
frame[key] = {
value: frame[key],
description: 'Maximum power yesterday',
units: 'W'
}
break
case 'ERR':
frame[key] = {
value: frame[key],
description: 'Error code',
units: ''
}
break
case 'CS':
frame[key] = {
value: frame[key],
description: 'State of operation',
units: ''
}
break
case 'BMV':
frame[key] = {
value: frame[key],
description: 'Model description (deprecated)',
units: ''
}
break
case 'FW':
frame[key] = {
value: frame[key],
description: 'Firmware version (16 bit)',
units: ''
}
break
case 'FWE':
frame[key] = {
value: frame[key],
description: 'Firmware version (24 bit)',
units: ''
}
break
case 'PID':
frame[key] = {
value: frame[key],
product: products[frame[key]],
description: 'ProductID',
units: ''
}
break
case 'SER#':
frame[key] = {
value: frame[key],
description: 'Serial number',
units: ''
}
break
case 'HSDS':
frame[key] = {
value: frame[key],
description: 'Day sequence number (0..364)',
units: ''
}
break
case 'MODE':
frame[key] = {
value: frame[key],
description: 'Device mode',
units: ''
}
break
case 'AC_OUT_V':
frame[key] = {
value: frame[key],
description: 'AC output voltage',
units: '0.01 V'
}
break
case 'AC_OUT_I':
frame[key] = {
value: frame[key],
description: 'AC output current',
units: '0.1 A'
}
break
case 'AC_OUT_S':
frame[key] = {
value: frame[key],
description: 'AC output apparent power',
units: 'VA'
}
break
case 'WARN':
frame[key] = {
value: frame[key],
description: 'Warning reason',
units: ''
}
break
case 'MPPT':
frame[key] = {
value: frame[key],
description: 'Tracker operation mode',
units: ''
}
break
case 'MON':
frame[key] = {
value: frame[key],
description: 'DC monitor mode',
units: ''
}
break
case 'DC_IN_V':
frame[key] = {
value: frame[key],
description: 'DC input voltage',
units: '0.01 V'
}
break
case 'DC_IN_I':
frame[key] = {
value: frame[key],
description: 'DC input current',
units: '0.1 A'
}
break
case 'DC_IN_P':
frame[key] = {
value: frame[key],
description: 'DC input power',
units: '1 W'
}
break
default:
frame[key] = {
value: frame[key],
description: 'Non implemented key',
units: ''
}
break
}
frame[key].value = parseFloat(frame[key].value) || frame[key].value
if (frame[key].value === '0') {
frame[key].value = 0
}
}
return frame
}
class VEDirectParser extends Transform {
constructor () {
super({
readableObjectMode: true
})
this.buf = Buffer.alloc(0)
this.blk = {}
}
_transform (chunk, _, cb) {
const [key, val] = chunk.toString().split('\t')
if (key[0] === ':') {
return cb()
}
this.buf = Buffer.concat([this.buf, Buffer.from([0x0d, 0x0a]), chunk])
if (key === 'Checksum') {
if (checksum(this.buf) === 0) {
this.push(parseValues(this.blk))
}
this.buf = Buffer.alloc(0)
this.blk = {}
} else {
this.blk[key] = val
}
cb()
}
}
module.exports = VEDirectParser
module.exports.checksum = checksum