Skip to content

Commit

Permalink
Fix in getting a unique deviceinstance
Browse files Browse the repository at this point in the history
Some services can't cope with a changing DeviceInstance, so instead
of changing it, first check which ones are already used and claim
the first one that isn't used.
Note that checking is done by looking at what has been claimed in
com.victronenergy.settings, not by actually looking at what the
services use.
  • Loading branch information
dirkjanfaber committed Nov 11, 2024
1 parent 874d764 commit 1ef08cf
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 2 deletions.
63 changes: 63 additions & 0 deletions src/nodes/config-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,69 @@ module.exports = function (RED) {
return res.send(serialized)
})

// Track last assigned instance numbers
const lastAssignedInstances = new Map()

function findNextAvailableInstance (jsonStr, deviceType) {
const data = JSON.parse(jsonStr)
const victronSettings = data['com.victronenergy.settings']

if (!victronSettings) {
return null
}

// Get all used instance numbers for the specified device type
const usedInstances = new Set()

function collectInstances (obj) {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object' && value !== null) {
collectInstances(value)
} else if (
key.endsWith('ClassAndVrmInstance') &&
typeof value === 'string' &&
value.startsWith(deviceType + ':')
) {
const instance = parseInt(value.split(':')[1])
if (!isNaN(instance)) {
usedInstances.add(instance)
}
}
}
}

collectInstances(victronSettings)

// Get the last assigned number for this device type, or start at 99
let nextInstance = (lastAssignedInstances.get(deviceType) || 99) + 1

// Keep incrementing until we find an unused number
while (usedInstances.has(nextInstance)) {
nextInstance++
}

// Store this assignment for future requests
lastAssignedInstances.set(deviceType, nextInstance)

return nextInstance
}

RED.httpNode.get('/victron/deviceinstance/:type', RED.auth.needsPermission('victron-client.read'), (req, res) => {
try {
const serialized = JSON.stringify(globalClient.system.cache)
const nextInstance = findNextAvailableInstance(serialized, req.params.type)

if (nextInstance === null) {
return res.status(404).json({ error: 'No settings found' })
}

res.setHeader('Content-Type', 'application/json')
return res.json({ instance: nextInstance })
} catch (error) {
console.error('Error finding next instance:', error)
return res.status(500).json({ error: 'Internal server error' })
}
})
/**
* Victron Energy Configuration Node.
*
Expand Down
18 changes: 17 additions & 1 deletion src/nodes/victron-virtual.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@
const selected = $('select#node-input-device').val()
$('.input-'+selected).show()

if (selected) {
$.getJSON('victron/deviceinstance/' + selected)
.done(function(response) {
if (response && typeof response.instance !== 'undefined') {
// Ensure instance is converted to string
const instanceStr = String(response.instance);
$('#node-input-instance').val(instanceStr);
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.error('Failed to fetch instance:', textStatus, errorThrown);
RED.notify("Error fetching instance number: " + textStatus, "error");
});
}

if (selected === 'temperature') {
// Show/hide battery voltage input based on checkbox
$('#node-input-include-battery').off('change').on('change', function() {
Expand All @@ -35,14 +50,14 @@
}
}


RED.nodes.registerType('victron-virtual',{
category: 'Victron Energy',
color: '#f7ab3e',
paletteLabel: "Virtual Device",
defaults: {
name: {value:""},
device: {value:"", required: true},
instance: {value:"", required: true},
// grid
grid_nrofphases: {value: 1, required: false},
// tank
Expand Down Expand Up @@ -76,6 +91,7 @@
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<input type="hidden" id="node-input-instance">
<div class="form-row">
<label for="node-input-device"><i class="fa fa-microchip"></i> Device</label>
<select id="node-input-device" required onchange="checkSelectedVirtualDevice()">
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/victron-virtual.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ module.exports = function (RED) {
const iface = getIface(device)

// Initially set the DeviceInstance to 100, this may be updated later if it has already been taken
iface.DeviceInstance = 100
iface.DeviceInstance = Number(config.instance) || 199
iface.CustomName = config.name || `Virtual ${config.device}`
iface.Status = 0
iface.Serial = id || '-'
Expand Down

0 comments on commit 1ef08cf

Please sign in to comment.