Skip to content

Commit

Permalink
Add new virtual devices
Browse files Browse the repository at this point in the history
Underlying dbus-victron-virtual library update to version 0.1.4,
which adds new functionality. Now calling addSettings before creating the service on the dbus. This allows for getting a correct DeviceInstance _before_ the service gets created. Allowing for quite a big cleanup of the code.
Also adding new virtual devices: battery and pv inverter.

And added the option to set default values for the node, which will result in the device to appear on VRM, without you having to write all needed values first.
  • Loading branch information
dirkjanfaber committed Dec 10, 2024
1 parent 3d7a156 commit 2a0f7b6
Show file tree
Hide file tree
Showing 7 changed files with 686 additions and 723 deletions.
11 changes: 5 additions & 6 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@ Output nodes let you control Victron devices.


### Virtual Devices
Virtual devices create simulated Victron devices on the system.
Virtual devices create simulated Victron devices on the system, which
will appear on VRM.

At the moment there are 4 different virtual devices available:
- Grid meter
- Meteo
- Tank sensor
- Temperature sensor
At the moment there are several different virtual devices available,
including grid meter, temperature sensor, pv inverter and meteo.
Check the documentation of the node itself for more info.

#### Virtual Device Usage
1. Add a virtual device node to your flow
Expand Down
1,042 changes: 449 additions & 593 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.6.3",
"dependencies": {
"dbus-native-victron": "^0.4.2",
"dbus-victron-virtual": "^0.1.3",
"dbus-victron-virtual": "^0.1.4",
"debug": "^4.3.7",
"lodash": "^4.17.21",
"promise-retry": "^2.0.1",
Expand Down
63 changes: 0 additions & 63 deletions src/nodes/config-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,69 +31,6 @@ 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
73 changes: 56 additions & 17 deletions src/nodes/victron-virtual.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,11 @@

function checkSelectedVirtualDevice() {
[
'grid', 'tank', 'temperature'
'battery', 'grid', 'pvinverter', 'tank', 'temperature'
].map( x => { $('.input-'+x).hide() })
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 @@ -57,9 +42,13 @@
defaults: {
name: {value:""},
device: {value:"", required: true},
instance: {value:"", required: true},
default_values: {value: false, required: false},
// battery
battery_capacity: {value: 25, required: false},
// grid
grid_nrofphases: {value: 1, required: false},
// pvinverter
position: { value: 0, required: false},
// tank
fluid_type: { value: 0, required: false},
include_tank_battery: { value: false },
Expand Down Expand Up @@ -95,12 +84,20 @@
<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()">
<option value="battery">Battery</option>
<option value="grid">Grid meter</option>
<option value="meteo">Meteo</option>
<option value="pvinverter">PV inverter</option>
<option value="tank">Tank sensor</option>
<option value="temperature">Temperature sensor</option>
</select>
</div>
<div class="input-battery">
<div class="form-row" id="battery-capacity">
<label for="node-input-battery_capacity"><i class="fa fa-flask"></i> Battery Capacity (Ah)</label>
<input type="number" id="node-input-battery_capacity" min="0">
</div>
</div>
<div class="input-grid">
<div class="form-tips" style="display: block;">
<p><strong>Note:</strong> Do not use this feature to make an ESS system with third party energy meters. There are multiple <a href="https://www.victronenergy.com/meters-and-sensors/energy-meter" class="blue-link">officially supported meters</a> that can be used in a Victron ESS system. Using other meters has proven too often to lead to issues related with stability.</p>
Expand Down Expand Up @@ -159,6 +156,16 @@
<input type="number" id="node-input-tank_battery_voltage" style="width:100px;" value="3.3" step="0.1" min="0">
</div>
</div>
<div class="input-pvinverter">
<div class="form-row">
<label for="node-input-position"><i class="fa fa-sliders"></i> Position</label>
<select id="node-input-position">
<option value="0">AC input 1</option>
<option value="1">AC output</option>
<option value="2">AC input 2</option>
</select>
</div>
</div>
<div class="input-temperature">
<div class="form-row">
<label for="node-input-temperature_type"><i class="fa fa-thermometer-half"></i> Temperature type</label>
Expand Down Expand Up @@ -199,6 +206,18 @@
<input type="number" id="node-input-temp_battery_voltage" style="width:100px;" value="3.3" step="0.1" min="0">
</div>
</div>
<div class="form-row">
<label>&nbsp;</label>
<div style="width:70%">
<label for="node-input-default_values" style="width:100%">
<input type="checkbox" id="node-input-default_values" style="display:inline-block; width:22px; vertical-align:baseline;">
Initialize with default values
</label>
<div class="form-tips" style="margin-top:3px;">
Use this to make the device appear on VRM immediately. You can change these values later.
</div>
</div>
</div>
</script>

<script type="text/markdown" data-help-name="victron-virtual">
Expand All @@ -212,8 +231,10 @@

To get started, pull a virtual device to the canvas and configure it. First the
device type needs to be selected. At the moment the system supports:
- Battery
- Grid meter
- Meteo
- PV inverter
- Tank sensor
- Temperature sensor

Expand All @@ -237,6 +258,17 @@
make its use more convenient. For other devices, the edit panel allows you to
enable or disable some of the paths.

Depending on the created device, it might not appear on VRM directly. You will
need to write some values to it first. Alternative, you could use _initialize
with defaults_ to make it appear on VRM directly. Do note that those default
values probably are not the one that make a lot of sense for your specific
situation. But it can help to get you started.

### Battery

The battery device allows for setting the capacity of the battery. The capacity
is configured in Ah.

### Grid meter

For the grid meter, you wil need to select the number of phases that are
Expand All @@ -254,6 +286,13 @@
would use it to send data from your weather stations to both a virtual meteo
device and a virtual temperature sensor.

### PV inverter

For the PV inverter you can set the position of the inverter. This can be:
- AC input 1
- AC output
- AC input 2

### Tank sensor

For the tank sensor you can pre-define some values, like the liquid type and
Expand Down
Loading

0 comments on commit 2a0f7b6

Please sign in to comment.