Skip to content

Commit

Permalink
Add digital output control node
Browse files Browse the repository at this point in the history
For supporting the i/o extender and writing to digital outputs of
the device.
  • Loading branch information
dirkjanfaber committed Oct 9, 2024
1 parent 68b6fbf commit 03b7f2d
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 2 deletions.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
"dependencies": {
"dbus-native": "^0.4.0",
"debug": "^4.3.4",
"exceljs": "^4.4.0",
"lodash": "^4.17.21",
"promise-retry": "^2.0.1",
"standard": "^17.1.0"
"standard": "^17.1.0",
"yargs": "^17.7.2"
},
"node-red": {
"nodes": {
"victron-client": "./src/nodes/config-client.js",
"victron-nodes": "./src/nodes/victron-nodes.js"
"victron-nodes": "./src/nodes/victron-nodes.js",
"victron-gpio": "./src/nodes/victron-gpio.js"
},
"version": ">=3.0.2"
},
Expand Down
105 changes: 105 additions & 0 deletions src/nodes/victron-gpio.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<script type="text/javascript">
RED.nodes.registerType('digital-output',{
category: 'Victron Energy',
color: '#4790d0',
defaults: {
name: {value:""},
output: {value:"", required:true}
},
inputs:1,
outputs:0,
icon: "victronenergy.svg",
label: function() {
return this.name || "Digital Output Control";
},
paletteLabel: "Digital Output Control",
oneditprepare: function() {
var node = this;
$.getJSON('gpio-outputs/'+this.id, function(data) {
var select = $('#node-input-output');
var warning = $('#node-warning');
select.empty();
if (data.outputs && data.outputs.length > 0) {
$.each(data.outputs, function(i, output) {
select.append($("<option></option>")
.attr("value", output)
.text("Output " + output));
});
select.val(node.output);
warning.hide();
} else {
select.hide();
warning.show();
var warningText = "<strong>Warning:</strong> NODE_RED_DBUS_ADDRESS is set";
if (data.dbusAddress) {
warningText += ` to "${data.dbusAddress}"`;
}
warningText += ". This node will not function, and no digital outputs are available. <br>Make sure NODE_RED_DBUS_ADDRESS is not set for this node to function.";
warning.html(warningText);
}
}).fail(function() {
var select = $('#node-input-output');
var warning = $('#node-warning');
select.hide();
warning.show();
warning.html("<strong>Warning:</strong> Unable to fetch digital outputs. NODE_RED_DBUS_ADDRESS might be set, preventing this node from functioning. <br>Ensure NODE_RED_DBUS_ADDRESS is not set for this node to function.");
});
}
});
</script>

<script type="text/html" data-template-name="digital-output">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-output"><i class="fa fa-dot-circle-o"></i> Output</label>
<select id="node-input-output">
<!-- Options will be populated dynamically -->
</select>
</div>
<div class="form-row">
<div id="node-warning" style="display: none; color: #ff8c00; margin-top: 10px;"></div>
</div>
</script>

<script type="text/markdown" data-help-name="digital-output">
# Digital Output Control

This node writes a 0 or 1 to a selected digital output.

## Important Note

This node requires the `NODE_RED_DBUS_ADDRESS` environment variable to NOT be set. If it's set, the node will not function.

## Input

The input should be one of the following:
- Boolean: `true` or `false`
- Number: `0` or `1`
- String: `"0"` or `"1"`

Any truthy value will be written as "1", and any falsy value will be written as "0".

## Configuration

- **Name**: Optional name for the node
- **Output**: Select an available digital output from the dropdown list

## Details

The node checks for available digital outputs in the `/run/io-ext/gpiochip388/output_[1-9]/value` directory.

When it receives an input, it writes the corresponding value to the selected digital output file.

## Status

- Green dot: Last written output and value (when successful)
- Red ring: Indicates that `NODE_RED_DBUS_ADDRESS` is set (node will not function)

## Error Handling

- If `NODE_RED_DBUS_ADDRESS` is set, the node will show a warning with the set value and won't function.
- If an invalid output is selected or there's an error writing to the file, the node will report an error.
</script>
80 changes: 80 additions & 0 deletions src/nodes/victron-gpio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module.exports = function(RED) {
function DigitalOutputNode(config) {
RED.nodes.createNode(this, config);
var node = this;

// Check if NODE_RED_DBUS_ADDRESS is set
if (process.env.NODE_RED_DBUS_ADDRESS) {
const errorMsg = `NODE_RED_DBUS_ADDRESS is set to "${process.env.NODE_RED_DBUS_ADDRESS}". This node will not function.`;
node.status({fill:"red", shape:"ring", text:"DBUS Address set"});
node.error(errorMsg);
return; // Exit the function early
}

// Initialize node properties
node.gpioPath = '/run/io-ext/gpiochip388';
node.availableOutputs = [];

// Function to check available GPIO outputs
function checkAvailableOutputs() {
const fs = require('fs');
const path = require('path');

node.availableOutputs = [];

for (let i = 1; i <= 9; i++) {
const filePath = path.join(node.gpioPath, `output_${i}`, 'value');
if (fs.existsSync(filePath)) {
node.availableOutputs.push(i);
}
}

// Update the dropdown options in the Node-RED editor
node.context().set('availableOutputs', node.availableOutputs);
RED.comms.publish('gpio-outputs', { id: node.id, outputs: node.availableOutputs, dbusAddress: process.env.NODE_RED_DBUS_ADDRESS });
}

// Check available outputs on node initialization
checkAvailableOutputs();

// Handle incoming messages
node.on('input', function(msg) {
const fs = require('fs');
const path = require('path');

const outputNumber = parseInt(config.output);
const value = (msg.payload === true || msg.payload === 1 || msg.payload === '1') ? '1' : '0';

if (node.availableOutputs.includes(outputNumber)) {
const filePath = path.join(node.gpioPath, `output_${outputNumber}`, 'value');

fs.writeFile(filePath, value, (err) => {
if (err) {
node.error(`Error writing to GPIO ${outputNumber}: ${err}`);
} else {
node.status({fill:"green", shape:"dot", text:`Output ${outputNumber}: ${value}`});
}
});
} else {
node.error(`Invalid output number: ${outputNumber}`);
}
});

// Clean up on close
node.on('close', function() {
// Add any cleanup code here if needed
});
}

RED.nodes.registerType("digital-output", DigitalOutputNode);

// Serve the custom node configuration UI
RED.httpAdmin.get("/gpio-outputs/:id", RED.auth.needsPermission("digital-output.read"), function(req, res) {
var node = RED.nodes.getNode(req.params.id);
if (node && node.availableOutputs) {
res.json({outputs: node.availableOutputs, dbusAddress: process.env.NODE_RED_DBUS_ADDRESS});
} else {
res.status(404).end();
}
});
}

0 comments on commit 03b7f2d

Please sign in to comment.