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..98825c04
--- /dev/null
+++ b/ui/src/widgets/ui-audio/UIAudio.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+