Skip to content

Commit

Permalink
feature: add ability to connect via VRM (#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbender9 authored Sep 9, 2024
1 parent 14a2dfd commit b15ed49
Showing 1 changed file with 124 additions and 42 deletions.
166 changes: 124 additions & 42 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const _ = require('lodash')
const mqtt = require('mqtt');
const createDbusListener = require('./dbus-listener')
const venusToDeltas = require('./venusToDeltas')

const vrmApiUrl = 'https://vrmapi.victronenergy.com'
const gpsDestination = 'com.victronenergy.gps'

const supportedMQTTTypes = [ 'battery', 'solarcharger', 'tank', 'vebus', 'inverter', 'temperature' ]
Expand Down Expand Up @@ -48,12 +48,13 @@ module.exports = function (app) {
installType: {
type: 'string',
title: 'How to connect to Venus D-Bus',
enum: ['mqtt', 'mqtts', 'local', 'remote'],
enum: ['mqtt', 'mqtts', 'local', 'remote', 'vrm'],
enumNames: [
'Connect to remote Venus installation via MQTT (Plain text)',
'Connect to remote Venus installation via MQTT (SSL)',
'Connect to localhost via dbus (signalk-server is running on a Venus device)',
'Connect to remote Venus installation via dbus'
'Connect to remote Venus installation via dbus',
'Connect to remote Venus installation via VRM'
],
default: 'mqtt'
},
Expand All @@ -76,6 +77,23 @@ module.exports = function (app) {
}
}
},
VRM: {
type: 'object',
properties: {
portalId: {
type: 'string',
title: 'VRM Portal Id',
},
userName: {
type: 'string',
title: 'VRM Email',
},
password: {
type: 'string',
title: 'VRM Password',
}
}
},
pollInterval: {
type: 'number',
title: 'Interval (in seconds) to poll venus for current values',
Expand Down Expand Up @@ -189,6 +207,11 @@ module.exports = function (app) {
password: {
'ui:widget': 'password'
}
},
VRM: {
password: {
'ui:widget': 'password'
}
}
}
}
Expand Down Expand Up @@ -284,7 +307,7 @@ module.exports = function (app) {
sendMeta(options.relayPath1, { displayName: options.relayDisplayName1 })
}

if ( options.installType === 'mqtt' || options.installType === 'mqtts' ) {
if ( options.installType === 'mqtt' || options.installType === 'mqtts' || options.installType === 'vrm' ) {
startMQTT(options, toDelta)
} else {
this.connect(options, toDelta)
Expand Down Expand Up @@ -386,35 +409,105 @@ module.exports = function (app) {
}
}

function startMQTT(options, toDelta) {
var host = options.MQTT.host

if ( !host || !host.length ) {
app.setPluginError('no host configured')
return
function getVRMBrokerHost(portalId) {
let sum = 0;

for ( let idx = 0; idx < portalId.length; idx++ ) {
let c = portalId.charCodeAt(idx)
sum += c;
}
let broker_index = sum % 128;
return `mqtt${broker_index}.victronenergy.com`
}

const url = `${options.installType}://${host}`
function setupSubscription(options, client) {
client.publish(`R/${plugin.portalID}/system/0/Serial`)
client.subscribe(`N/${plugin.portalID}/+/#`)
if ( options.pollInterval !== -1 ) {
if ( pollInterval ) {
clearInterval(pollInterval)
}
pollInterval = setInterval(() => {
app.debug('resending deltas...')
resendDeltas()
}, options.pollInterval*1000)
if ( keepAlive ) {
clearInterval(keepAlive)
}
keepAlive = setInterval(() => {
app.debug('sending keep alive')
client.publish(`R/${plugin.portalID}/system/0/Serial`)
client.subscribe(`N/${plugin.portalID}/+/#`)
}, 50*1000)
}
}

app.debug('using mqtt url %s', url)
function startMQTT(options, toDelta) {
var host
var port
var scheme
var username
var password
const isVRM = options.installType === 'vrm'

var client = mqtt.connect(url, {
if ( isVRM ) {
host = getVRMBrokerHost(options.VRM.portalId)
port = 8883
scheme = 'mqtts'
username = options.VRM.userName
password = options.VRM.password
} else {
host = options.MQTT.host
port = options.installType === 'mqtt' ? 1883 : 8883
scheme = options.installType
password = options.MQTT.password
username = ''

if ( !host || !host.length ) {
app.setPluginError('no host configured')
return
}
}

const url = `${scheme}://${host}:${port}`

app.debug('using mqtt url %s', url)

let connectOptions = {
rejectUnauthorized: false,
username: '',
password: options.MQTT.password
})
}

//if ( password && password.length )
{
connectOptions.username = username
connectOptions.password = password
}

var client = mqtt.connect(url, connectOptions)
plugin.client = client

plugin.needsID = true
plugin.portalID = null
if ( isVRM ) {
plugin.portalID = options.VRM.portalId
plugin.needsID = false
} else {
plugin.needsID = true
plugin.portalID = null
}

app.debug(`connecting to ${url}`)

client.on('connect', function () {
app.debug(`connected to ${url}`)
client.subscribe('N/+/+/#')
app.setPluginStatus(`Connected to ${url}`)

if ( isVRM ) {
setupSubscription(options, client)
} else {
client.subscribe('N/+/+/#')
}


//client.publish(`R/${portalID}/system/0/Serial`)

//client.subscribe(`N/${portalID}/+/#`)
Expand All @@ -429,8 +522,10 @@ module.exports = function (app) {
sentDeltas = {}
customNames = {}
customNameTimeouts = {}
plugin.needsID = true
plugin.portalID = null
if ( isVRM === false ) {
plugin.needsID = true
plugin.portalID = null
}
app.debug(`mqtt close`)
});

Expand Down Expand Up @@ -479,30 +574,17 @@ module.exports = function (app) {

if ( plugin.needsID ) {
if ( topic.endsWith('system/0/Serial') ) {
plugin.portalID = message.value
app.debug('detected portalId %s', plugin.portalID)
client.publish(`R/${plugin.portalID}/system/0/Serial`)
client.subscribe(`N/${plugin.portalID}/+/#`)
plugin.needsID = false
if ( options.pollInterval !== -1 ) {
if ( pollInterval ) {
clearInterval(pollInterval)
}
pollInterval = setInterval(() => {
app.debug('resending deltas...')
resendDeltas()
}, options.pollInterval*1000)
if ( keepAlive ) {
clearInterval(keepAlive)
}
keepAlive = setInterval(() => {
app.debug('sending keep alive')
client.publish(`R/${plugin.portalID}/system/0/Serial`)
client.subscribe(`N/${plugin.portalID}/+/#`)
}, 50*1000)
if ( !isVRM ) {
plugin.portalID = message.value
app.debug('detected portalId %s', plugin.portalID)
}
plugin.needsID = false
setupSubscription(options, client)
}

if ( !isVRM ) {
return
}
return
}

let senderName = `com.victronenergy.${type}.${instance}`
Expand Down

0 comments on commit b15ed49

Please sign in to comment.