@liball/node-red-contrib-opi-gpio
Version:
Orange Pi GPIO. Digital input/output for Orange Pi
267 lines (242 loc) • 14 kB
HTML
<script type="text/html" data-template-name="opi_out">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> 名称</label>
<input type="text" id="node-input-name" placeholder="例如:控制 LED">
</div>
<div class="form-row">
<label for="node-input-select"><i class="fa fa-microchip"></i> 设备型号</label>
<select id="node-input-select" style="width: 70%;">
<option value="" disabled>-- 选择设备型号 --</option>
<option value="opz2w">Orange Pi Zero 2W</option>
</select>
</div>
<div class="form-row" id="device-pinout-image-container" style="text-align: center; display: none; margin-top: 10px; border: 1px dashed #ccc; padding: 5px;">
<label style="display: block; margin-bottom: 5px;">引脚布局图:</label>
<img id="device-pinout-image" src="" alt="设备引脚图" style="max-width: 95%; height: auto;"/>
</div>
<div id="opz2w-pin-options" style="display: none;"> <div class="form-row">
<label for="node-input-pin-opz2w"><i class="fa fa-tag"></i> GPIO 引脚</label>
<select id="node-input-pin-opz2w" style="width: 70%;">
<option value='' disabled selected style='display:none;'>选择引脚 或 自定义</option>
<option value="264">PI8 (264)</option>
<option value="263">PI7 (263)</option>
<option value="269">PI13 (269)</option>
<option value="224">PH0 (224)</option>
<option value="225">PH1 (225)</option>
<option value="257">PI1 (257)</option>
<option value="226">PH2 (226)</option>
<option value="227">PH3 (227)</option>
<option value="261">PI5 (261)</option>
<option value="270">PI14 (270)</option>
<option value="228">PH4 (228)</option>
<option value="231">PH7 (231)</option>
<option value="232">PH8 (232)</option>
<option value="262">PI6 (262)</option>
<option value="230">PH6 (230)</option>
<option value="229">PH5 (229)</option>
<option value="233">PH9 (233)</option>
<option value="266">PI10 (266)</option>
<option value="265">PI9 (265)</option>
<option value="256">PI0 (256)</option>
<option value="271">PI15 (271)</option>
<option value="267">PI11 (267)</option>
<option value="268">PI12 (268)</option>
<option value="258">PI2 (258)</option>
<option value="76">PC12 (76)</option>
<option value="272">PI16 (272)</option>
<option value="260">PI4 (260)</option>
<option value="259">PI3 (259)</option>
<option value="custom">-- 自定义/环境变量 --</option>
</select>
</div>
<div class="form-row" id="custom-pin-input-row" style="display: none;">
<label for="node-input-pin-custom"><i class="fa fa-pencil"></i> 自定义值</label>
<input type="text" id="node-input-pin-custom" style="width: 70%;">
<input type="hidden" id="node-input-pinType-custom"> </div>
</div>
<div class="form-row" id="node-set-tick">
<label> </label>
<input type="checkbox" id="node-input-set" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-set" style="width: 70%;">初始化引脚状态?</label>
</div>
<div class="form-row" id="node-set-state" style="display: none;">
<label for="node-input-level"><i class="fa fa-level-down"></i> 初始电平</label>
<select id="node-input-level" style="width: 70%;">
<option value="0"> 低电平 (0)</option>
<option value="1"> 高电平 (1)</option>
</select>
</div>
</script>
<script type="text/html" data-help-name="opi_out">
<p>GPIO <b>输出</b> 节点,用于兼容多种设备(当前主要支持 <b>Orange Pi Zero 2W</b>)。</p>
<p>请先从“设备型号”下拉列表中选择您的设备。根据所选设备,下方会显示引脚布局图和可用的 GPIO 引脚列表。</p>
<p>选择要控制的 GPIO 引脚,或选择“-- 自定义/环境变量 --”来手动输入引脚号、环境变量名 (例如 <code>MY_PIN_VAR</code>) 或其他支持的类型。</p>
<p>如果使用环境变量,请在子流程属性或 Node-RED 运行环境中定义它。</p>
<p>用户有责任确保所选或输入的引脚号有效且不会干扰其他系统功能。</p>
<p>输入的 <code>msg.payload</code> 应设置为 <code>1</code> (高电平) 或 <code>0</code> (低电平) 来控制引脚状态。</p>
<p><b>初始状态:</b> 您可以选择在部署或启动时初始化引脚的状态。勾选 "初始化引脚状态?" 并选择所需的初始电平 (高或低)。</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('opi_out', {
category: 'Orange Pi', // Or your desired category
color: '#FFCC66', // Orange-ish color
defaults: {
name: {value: ""},
select: {value: "opz2w"}, // Default device
pin: {value: "", required: true}, // Stores final pin value (number or string/var name)
pinSource: {value: "list"}, // Source: 'list' or 'custom'
pinType: {value: "num"}, // Type when pinSource is 'custom' ('num', 'str', 'env'...)
set: {value: false}, // Initialize pin state?
level: {value: "0"} // Initial pin level (0 or 1)
},
inputs: 1,
outputs: 0,
icon: "out.png", // Provide an icon file or use a Font Awesome icon e.g., "fa-lightbulb-o"
paletteLabel: "GPIO OUT",
label: function () {
// Generate a dynamic label for the node
let pinDisplay = this.pin;
if (this.pinSource === 'custom') {
switch (this.pinType) {
case 'env': pinDisplay = '${' + this.pin + '}'; break;
case 'flow': pinDisplay = '#' + this.pin; break; // Assuming pin stores full path for flow/global
case 'global': pinDisplay = '#' + this.pin; break;
// Add other types if needed
}
}
const deviceLabel = this.select === 'opz2w' ? 'OPIZ2W' : (this.select || '设备');
return this.name || (pinDisplay ? `${deviceLabel} GPIO ${pinDisplay}` : "GPIO OUT");
},
oneditprepare: function () {
const node = this; // Reference to the node object for callbacks
// --- IMPORTANT: Set your node's registered name here for image paths ---
const resourcePath = "resources/@liball/node-red-contrib-opi-gpio";
// ---------------------------------------------------------------------
// Get jQuery elements
const $deviceSelect = $("#node-input-select");
const $imageContainer = $("#device-pinout-image-container");
const $imageElement = $("#device-pinout-image");
const $opz2wOptions = $("#opz2w-pin-options"); // Container for OPiZ2W specific inputs
const $pinSelect = $("#node-input-pin-opz2w"); // The dropdown list for pins
const $customPinRow = $("#custom-pin-input-row"); // The row for custom input
const $customPinInput = $("#node-input-pin-custom"); // The TypedInput element
const $customPinType = $("#node-input-pinType-custom"); // Hidden input for custom type
// Function to update UI based on selected device
const updateUIForDevice = () => {
const selectedDevice = $deviceSelect.val();
// Hide all device-specific options and image first
$opz2wOptions.hide();
// Add similar lines for other device option containers if you add them
$imageContainer.hide();
$imageElement.attr("src", ""); // Clear image
// --- Handle Orange Pi Zero 2W ---
if (selectedDevice === 'opz2w') {
// 1. Show OPiZ2W options container
$opz2wOptions.show();
// 2. Show Pinout Image
const imageUrl = `${resourcePath}/gpio-zero2w.png`; // Assumes image is in node dir
$imageElement.attr("src", imageUrl);
$imageContainer.show();
// 3. Handle Pin Input Mode (List vs Custom) based on saved state
if (node.pinSource === 'custom') {
$pinSelect.val('custom'); // Select "custom" in dropdown
$customPinRow.show(); // Show the custom input row
// Initialize TypedInput only once
if (!$customPinInput.data('typedInput')) {
$customPinInput.typedInput({
default: node.pinType || 'num',
types: ['num', 'str', 'env', 'flow', 'global'], // Add/remove types as needed
typeField: $customPinType // Link to the hidden type input
});
}
// Set initial value and type for TypedInput
$customPinInput.typedInput('value', node.pin);
$customPinInput.typedInput('type', node.pinType || 'num');
} else {
// Mode is 'list'
$pinSelect.val(node.pin); // Select the saved pin in the dropdown
$customPinRow.hide(); // Hide the custom input row
}
}
// --- Handle Other Devices (Example Structure) ---
// else if (selectedDevice === 'other_device') {
// // Show options for other_device
// // Show image for other_device
// // Handle pin input for other_device
// }
// --- Default case (no device selected or unknown) ---
else {
// All options and image remain hidden
}
};
// --- Event Handlers ---
// 1. Device selection changes
$deviceSelect.change(() => {
// When device changes, reset pin selection logic potentially needed
// For now, just update the whole UI
updateUIForDevice();
});
// 2. Pin dropdown selection changes
$pinSelect.change(function () {
const selectedValue = $(this).val();
if (selectedValue === 'custom') {
// Show custom input row
$customPinRow.show();
// Initialize TypedInput if it hasn't been already
if (!$customPinInput.data('typedInput')) {
$customPinInput.typedInput({
default: 'num', // Default to number when switching to custom
types: ['num', 'str', 'env', 'flow', 'global'],
typeField: $customPinType
});
// Optionally clear the custom input when switched to
// $customPinInput.typedInput('value', '');
}
} else {
// Hide custom input row if a specific pin is selected
$customPinRow.hide();
}
});
// 3. Initial state checkbox changes
const setstate = function () {
if ($('#node-input-set').is(":checked")) {
$("#node-set-state").show();
} else {
$("#node-set-state").hide();
}
};
$("#node-input-set").change(setstate); // Attach change handler
// --- Initialization on Dialog Open ---
// Set initial values from node properties
$deviceSelect.val(node.select || 'opz2w'); // Set device dropdown
$("#node-input-set").prop('checked', node.set); // Set init state checkbox
$("#node-input-level").val(node.level || "0"); // Set init state level dropdown
// Update the entire UI based on the loaded device and pin source
updateUIForDevice();
// Ensure the initial state level dropdown visibility is correct
setstate();
},
oneditsave: function () {
const node = this;
// Save device selection
node.select = $("#node-input-select").val();
// Determine pin source and save pin/pinType accordingly
const pinSelectionMode = $("#node-input-pin-opz2w").val(); // Check the dropdown
if (pinSelectionMode === 'custom') {
node.pinSource = 'custom';
// Get value and type from the TypedInput
node.pin = $("#node-input-pin-custom").typedInput('value');
node.pinType = $("#node-input-pin-custom").typedInput('type');
} else {
node.pinSource = 'list';
node.pin = pinSelectionMode; // Get value directly from dropdown
node.pinType = 'num'; // Pins from list are always numbers
}
// Note: 'set' and 'level' defaults are automatically saved by Node-RED
// because their input IDs match 'node-input-set' and 'node-input-level'
}
// oneditcancel: function() { ... } // Optional
// oneditdelete: function() { ... } // Optional
// oneditresize: function() { ... } // Optional
});
</script>