From c4831845021254ebae982c5652d8b3f2ce26ef82 Mon Sep 17 00:00:00 2001 From: bartbutenaers Date: Fri, 29 Nov 2024 08:57:46 +0100 Subject: [PATCH 1/2] new ui-audio widget --- docs/nodes/widgets.md | 7 +- docs/nodes/widgets/ui-audio.md | 76 ++++++++++++++ nodes/widgets/locales/en-US/ui_audio.html | 31 ++++++ nodes/widgets/locales/en-US/ui_audio.json | 17 +++ nodes/widgets/ui_audio.html | 113 ++++++++++++++++++++ nodes/widgets/ui_audio.js | 61 +++++++++++ package.json | 3 +- ui/src/widgets/index.mjs | 3 + ui/src/widgets/ui-audio/UIAudio.vue | 122 ++++++++++++++++++++++ 9 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 docs/nodes/widgets/ui-audio.md create mode 100644 nodes/widgets/locales/en-US/ui_audio.html create mode 100644 nodes/widgets/locales/en-US/ui_audio.json create mode 100644 nodes/widgets/ui_audio.html create mode 100644 nodes/widgets/ui_audio.js create mode 100644 ui/src/widgets/ui-audio/UIAudio.vue diff --git a/docs/nodes/widgets.md b/docs/nodes/widgets.md index 0cba85fa..1384e4ff 100644 --- a/docs/nodes/widgets.md +++ b/docs/nodes/widgets.md @@ -8,6 +8,11 @@ description: Explore the wide range of widgets available in Node-RED Dashboard 2 import WidgetGrid from '../components/WidgetGrid.vue' const general = [{ + name: 'Audio', + widget: 'ui-audio', + image: '/images/node-examples/ui-audio.png', + description: 'Adds a audio player to your dashboard.' + }, { name: 'Button', widget: 'ui-button', image: '/images/node-examples/ui-button.png', @@ -148,4 +153,4 @@ Here is a list of the third-party widgets we're aware of to make it easier to fi The following are a list of nodes that we've been made aware of, are in active development, but have not yet been published to the Node-RED Palette Manager. -- [@bartbutenaers/ui-svg](https://github.com/bartbutenaers/node-red-dashboard-2-ui-svg/tree/master): Adds an SVG widget to your Dashboard, with dynamic controls over plotting and styling. \ No newline at end of file +- [@bartbutenaers/ui-svg](https://github.com/bartbutenaers/node-red-dashboard-2-ui-svg/tree/master): Adds an SVG widget to your Dashboard, with dynamic controls over plotting and styling. diff --git a/docs/nodes/widgets/ui-audio.md b/docs/nodes/widgets/ui-audio.md new file mode 100644 index 00000000..976bdd33 --- /dev/null +++ b/docs/nodes/widgets/ui-audio.md @@ -0,0 +1,76 @@ +--- +description: "Play dynamically audio files with ui-audio in Node-RED Dashboard 2.0." +props: + Group: Defines which group of the UI Dashboard this widget will render in. + Size: Controls the width of the button with respect to the parent group. Maximum value is the width of the group. + Source: + description: The source is the url where the audio file can be fetched.. + dynamic: true + Autoplay: + description: Specify whether the audio file will start playing automatically. + dynamic: true + Loop: + description: Specify whether the audio should be looping, i.e. start playing automatically again when ended. + dynamic: true + Muted: + description: Specify whether the audio should be muted. + dynamic: true +controls: + enabled: + example: true | false + description: Allow control over whether or not the button is clickable. +dynamic: + Source: + payload: msg.ui_update.source + structure: ["String"] + Autoplay: + payload: msg.ui_update.autoplay + structure: ["'on' | 'off'"] + Loop: + payload: msg.ui_update.loop + structure: ["'on' | 'off'"] + Muted: + payload: msg.ui_update.muted + structure: ["'on' | 'off'"] +--- + + + + + + +# Audio `ui-audio` + + + +Adds a clickable button to your dashboard. + +## Properties + + + +## Dynamic Properties + + + +## Controls + + + +## Example + +### Simple Button + +![Example of a Button](/images/node-examples/ui-button.png "Example of a Button"){data-zoomable} +*Example of a rendered button in a Dashboard.* diff --git a/nodes/widgets/locales/en-US/ui_audio.html b/nodes/widgets/locales/en-US/ui_audio.html new file mode 100644 index 00000000..59503edc --- /dev/null +++ b/nodes/widgets/locales/en-US/ui_audio.html @@ -0,0 +1,31 @@ + diff --git a/nodes/widgets/locales/en-US/ui_audio.json b/nodes/widgets/locales/en-US/ui_audio.json new file mode 100644 index 00000000..8ad28eac --- /dev/null +++ b/nodes/widgets/locales/en-US/ui_audio.json @@ -0,0 +1,17 @@ +{ + "ui-audio": { + "label": { + "group": "Group", + "size": "Size", + "icon": "Icon", + "source": "Source", + "autoplay": "Autoplay", + "loop": "Loop", + "muted": "Muted" + }, + "option": { + "on": "On", + "off": "Off" + } + } +} diff --git a/nodes/widgets/ui_audio.html b/nodes/widgets/ui_audio.html new file mode 100644 index 00000000..4f48273e --- /dev/null +++ b/nodes/widgets/ui_audio.html @@ -0,0 +1,113 @@ + + + diff --git a/nodes/widgets/ui_audio.js b/nodes/widgets/ui_audio.js new file mode 100644 index 00000000..3f801bc4 --- /dev/null +++ b/nodes/widgets/ui_audio.js @@ -0,0 +1,61 @@ +const statestore = require('../store/state.js') + +module.exports = function (RED) { + function AudioNode (config) { + const node = this + + RED.nodes.createNode(this, config) + + // which group are we rendering this widget + const group = RED.nodes.getNode(config.group) + + const evts = { + onAction: true, + beforeSend: function (msg) { + if (msg.ui_update) { + const updates = msg.ui_update + + if (updates) { + if (typeof updates.src !== 'undefined') { + // dynamically set "src" property + statestore.set(group.getBase(), node, msg, 'src', updates.src) + } + if (typeof updates.autoplay !== 'undefined') { + if (['on', 'off'].includes(updates.autoplay)) { + // dynamically set "autoplay" property + statestore.set(group.getBase(), node, msg, 'autoplay', updates.autoplay) + } else { + node.error('Property msg.ui_update.autoplay should be "on" or "off"') + } + } + if (typeof updates.loop !== 'undefined') { + if (['on', 'off'].includes(updates.loop)) { + // dynamically set "loop" property + statestore.set(group.getBase(), node, msg, 'loop', updates.loop) + } else { + node.error('Property msg.ui_update.loop should be "on" or "off"') + } + } + if (typeof updates.muted !== 'undefined') { + if (['on', 'off'].includes(updates.muted)) { + // dynamically set "muted" property + statestore.set(group.getBase(), node, msg, 'muted', updates.muted) + } else { + node.error('Property msg.ui_update.muted should be "on" or "off"') + } + } + } + } + return msg + } + } + + // inform the dashboard UI that we are adding this node + if (group) { + group.register(node, config, evts) + } else { + node.error('No group configured') + } + } + RED.nodes.registerType('ui-audio', AudioNode) +} diff --git a/package.json b/package.json index 356b68a3..be970980 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,8 @@ "ui-markdown": "nodes/widgets/ui_markdown.js", "ui-template": "nodes/widgets/ui_template.js", "ui-event": "nodes/widgets/ui_event.js", - "ui-control": "nodes/widgets/ui_control.js" + "ui-control": "nodes/widgets/ui_control.js", + "ui-audio": "nodes/widgets/ui_audio.js" } }, "overrides": { diff --git a/ui/src/widgets/index.mjs b/ui/src/widgets/index.mjs index 47d49461..ae7da1de 100644 --- a/ui/src/widgets/index.mjs +++ b/ui/src/widgets/index.mjs @@ -1,3 +1,4 @@ +import UIAudio from './ui-audio/UIAudio.vue' import UIButton from './ui-button/UIButton.vue' import UIButtonGroup from './ui-button-group/UIButtonGroup.vue' import UIChart from './ui-chart/UIChart.vue' @@ -21,6 +22,7 @@ import UITextInput from './ui-text-input/UITextInput.vue' // Named exports for use in other components export { + UIAudio, UIButton, UIButtonGroup, UIChart, @@ -48,6 +50,7 @@ export { useDataTracker } from './data-tracker.mjs' // Exported as an object for look up by widget name export default { + 'ui-audio': UIAudio, 'ui-button': UIButton, 'ui-button-group': UIButtonGroup, 'ui-chart': UIChart, diff --git a/ui/src/widgets/ui-audio/UIAudio.vue b/ui/src/widgets/ui-audio/UIAudio.vue new file mode 100644 index 00000000..ede6fe1c --- /dev/null +++ b/ui/src/widgets/ui-audio/UIAudio.vue @@ -0,0 +1,122 @@ + + + + + From 95a9d8a0f43a00eddc8263f57ea8bafa9fe6ede0 Mon Sep 17 00:00:00 2001 From: bartbutenaers Date: Fri, 29 Nov 2024 09:21:55 +0100 Subject: [PATCH 2/2] play src from config --- ui/src/widgets/ui-audio/UIAudio.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/widgets/ui-audio/UIAudio.vue b/ui/src/widgets/ui-audio/UIAudio.vue index ede6fe1c..98825c04 100644 --- a/ui/src/widgets/ui-audio/UIAudio.vue +++ b/ui/src/widgets/ui-audio/UIAudio.vue @@ -42,7 +42,7 @@ export default { ...mapState('data', ['messages']), value: function () { // Get the value (i.e. the notification text content) from the last input msg - const value = this.messages[this.id]?.payload + const value = this.messages[this.id]?.payload || this.props.src return value }, loop () {