diff --git a/docs/components/DynamicPropsTable.vue b/docs/components/DynamicPropsTable.vue
index fc48e53c9..8d9dff03e 100644
--- a/docs/components/DynamicPropsTable.vue
+++ b/docs/components/DynamicPropsTable.vue
@@ -21,7 +21,7 @@
{{ value.structure[0] }}
-
+
{{ example }}
|
diff --git a/docs/nodes/widgets/ui-gauge.md b/docs/nodes/widgets/ui-gauge.md
index 3455a5132..575473373 100644
--- a/docs/nodes/widgets/ui-gauge.md
+++ b/docs/nodes/widgets/ui-gauge.md
@@ -3,19 +3,75 @@ description: Display real-time metrics with ui-gauge in Node-RED Dashboard 2.0 f
props:
Group: Defines which group of the UI Dashboard this widget will render in.
Size: Controls the width of the dropdown with respect to the parent group. Maximum value is the width of the group.
- Type: Defines the shape of the gauge, "Tile", "Battery", "Water Tank", "Half Gauge" or "3/4 Gauge"
- Style: Defines the style of arc rendered, "Needle" or "Rounded"
- Range (min): The smallest value that can be shown on the gauge
- Range (max): The largest value that can be shown on the gauge
- Segments: Defines the barriers by which the arc is color coded. These segments can also be shown on the gauge.
- Label: Text shown above the gauge, labelling what the gauge is showing.
- Prefix: Text to be added _before_ the value in the middle of the gauge.
- Suffix: Text to be shown _after_ the value in the middle of the gauge.
- Units: Small text to be shown below the value in the middle of the gauge.
- Icon: Icon to be shown below the value in the middle of the gauge. Uses Material Designs Icon, no need to include the mdi- prefix.
+ Type:
+ description: Defines the shape of the gauge, "Tile", "Battery", "Water Tank", "Half Gauge" or "3/4 Gauge"
+ dynamic: true
+ Style:
+ description: Defines the style of arc rendered, "Needle" or "Rounded"
+ dynamic: true
+ Range (min):
+ description: The smallest value that can be shown on the gauge
+ dynamic: true
+ Range (max):
+ description: The largest value that can be shown on the gauge
+ dynamic: true
+ Segments:
+ description: Defines the barriers by which the arc is color coded. These segments can also be shown on the gauge.
+ dynamic: true
+ Label:
+ description: Text shown above the gauge, labelling what the gauge is showing.
+ dynamic: true
+ Prefix:
+ description: Text to be added _before_ the value in the middle of the gauge.
+ dynamic: true
+ Suffix:
+ description: Text to be shown _after_ the value in the middle of the gauge.
+ dynamic: true
+ Units:
+ description: Small text to be shown below the value in the middle of the gauge.
+ dynamic: true
+ Icon:
+ description: Icon to be shown below the value in the middle of the gauge. Uses Material Designs Icon, no need to include the mdi- prefix.
+ dynamic: true
Sizes (Gauge): (px) How thick the arc and backdrop of the gauge are rendered.
Sizes (Gap): (px) How big the gap/padding is between the Gauge and the "Segments"
Sizes (Segments): (px) How thick the segments are rendered.
+controls:
+ enabled:
+ example: true | false
+ description: Allow control over whether or not the number-input is enabled
+dynamic:
+ Label:
+ payload: msg.ui_update.title
+ structure: ["String"]
+ Icon:
+ payload: msg.ui_update.icon
+ structure: ["String"]
+ Type:
+ payload: msg.ui_update.gtype
+ structure: ["String"]
+ examples: ['gauge-tile', 'gauge-battery', 'gauge-tank', 'gauge-half', 'gauge-34']
+ Style:
+ payload: msg.ui_update.gstyle
+ structure: ["String"]
+ Min:
+ payload: msg.ui_update.min
+ structure: ["Number"]
+ Max:
+ payload: msg.ui_update.max
+ structure: ["Number"]
+ Segments:
+ payload: msg.ui_update.segments
+ structure: ["Array<{color: String, from: Number}>"]
+ Prefix:
+ payload: msg.ui_update.prefix
+ structure: ["String"]
+ Suffix:
+ payload: msg.ui_update.suffix
+ structure: ["String"]
+ Units:
+ payload: msg.ui_update.units
+ structure: ["String"]
---
@@ -43,6 +99,14 @@ Values for the gauges can be set by sending a numerical value in `msg.payload`.
+## Dynamic Properties
+
+
+
+## Controls
+
+
+
## Examples
### Half Gauge - Rounded
diff --git a/nodes/widgets/locales/en-US/ui_gauge.html b/nodes/widgets/locales/en-US/ui_gauge.html
index 8bbb2a27d..611f37a08 100644
--- a/nodes/widgets/locales/en-US/ui_gauge.html
+++ b/nodes/widgets/locales/en-US/ui_gauge.html
@@ -28,4 +28,41 @@ Properties (Half & 3/4 Gauges Only)
Sizes (Segments) number
A numerical value, in pixels, that defines the thickness of the segments rendered in the gauge.
+ Dynamic Properties (Inputs)
+ Any of the following can be appended to a msg.ui_update in order to override or set properties on this node at runtime.
+
+ - title string
+ - Update the label rendered above the Gauge
+ - segments array
+ -
+ Change the options available in the dropdown at runtime
+
+ Array<{color: String, from: Number}>
+
+
+ - gstyle see detail
+ - Modify the type of Gauge rendered, with the following options:
+
+ gauge-battery
+ gauge-34
+ gauge-half
+ gauge-tile
+ gauge-tank
+
+
+ - min number
+ - Change the minimum value the gauge supports
+ - max number
+ - Change the maximum value the gauge supports
+ - prefix string
+ - Change the text rendered after the Gauge's value
+ - suffix string
+ - Change the text rendered after the Gauge's value
+ - units array
+ - Controls the "unit" display underneath the gauge's value for 3/4 and Half gauges.
+ - icon string
+ - Modify which icon is rendered within the gauge (must be a Material Design icon)
+ - class string
+ - Add a CSS class, or more, to the Button at runtime.
+
\ No newline at end of file
diff --git a/nodes/widgets/ui_gauge.js b/nodes/widgets/ui_gauge.js
index 56824cc28..2eb72a0e4 100644
--- a/nodes/widgets/ui_gauge.js
+++ b/nodes/widgets/ui_gauge.js
@@ -1,3 +1,6 @@
+const statestore = require('../store/state.js')
+const { appendTopic } = require('../utils/index.js')
+
module.exports = function (RED) {
function GaugeNode (config) {
RED.nodes.createNode(this, config)
@@ -7,7 +10,53 @@ module.exports = function (RED) {
const group = RED.nodes.getNode(config.group)
const evts = {
- onChange: true
+ beforeSend: async function (msg) {
+ const updates = msg.ui_update
+ if (updates) {
+ if (typeof updates.title !== 'undefined') {
+ // dynamically set "label" property
+ statestore.set(group.getBase(), node, msg, 'title', updates.title)
+ }
+ if (typeof updates.gtype !== 'undefined') {
+ // dynamically set "gauge type" property
+ statestore.set(group.getBase(), node, msg, 'gtype', updates.gtype)
+ }
+ if (typeof updates.gstyle !== 'undefined') {
+ // dynamically set "gauge style" property
+ statestore.set(group.getBase(), node, msg, 'gstyle', updates.gstyle)
+ }
+ if (typeof updates.prefix !== 'undefined') {
+ // dynamically set "prefix" property
+ statestore.set(group.getBase(), node, msg, 'prefix', updates.prefix)
+ }
+ if (typeof updates.suffix !== 'undefined') {
+ // dynamically set "suffix" property
+ statestore.set(group.getBase(), node, msg, 'suffix', updates.suffix)
+ }
+ if (typeof updates.units !== 'undefined') {
+ // dynamically set "units" property
+ statestore.set(group.getBase(), node, msg, 'units', updates.units)
+ }
+ if (typeof updates.icon !== 'undefined') {
+ // dynamically set "icon" property
+ statestore.set(group.getBase(), node, msg, 'icon', updates.icon)
+ }
+ if (typeof updates.segments !== 'undefined') {
+ // dynamically set "segments" property
+ statestore.set(group.getBase(), node, msg, 'segments', updates.segments)
+ }
+ if (typeof updates.min !== 'undefined') {
+ // dynamically set "min" property
+ statestore.set(group.getBase(), node, msg, 'min', updates.min)
+ }
+ if (typeof updates.max !== 'undefined') {
+ // dynamically set "max" property
+ statestore.set(group.getBase(), node, msg, 'max', updates.max)
+ }
+ }
+ msg = await appendTopic(RED, config, node, msg)
+ return msg
+ }
}
// ensure values are numerical, not strings
diff --git a/ui/src/widgets/ui-gauge/UIGauge.vue b/ui/src/widgets/ui-gauge/UIGauge.vue
index 2269cfd03..371289d93 100644
--- a/ui/src/widgets/ui-gauge/UIGauge.vue
+++ b/ui/src/widgets/ui-gauge/UIGauge.vue
@@ -1,8 +1,6 @@
-
-
-
-
+
+
diff --git a/ui/src/widgets/ui-gauge/types/UIGaugeDial.vue b/ui/src/widgets/ui-gauge/types/UIGaugeDial.vue
index 88f765fdd..dbccaea03 100644
--- a/ui/src/widgets/ui-gauge/types/UIGaugeDial.vue
+++ b/ui/src/widgets/ui-gauge/types/UIGaugeDial.vue
@@ -77,12 +77,27 @@ export default {
},
iconOnly () {
return this.props.icon && !this.props.units
+ },
+ gaugeStyle () {
+ return this.props.gstyle
+ },
+ updateOn () {
+ return `${this.segments} ${this.gaugeStyle} ${this.min} ${this.max}`
+ },
+ min () {
+ return this.props.min
+ },
+ max () {
+ return this.props.max
}
},
watch: {
- value: function (val, oldVal) {
+ value: function (val) {
this.resize()
this.update(val)
+ },
+ updateOn () {
+ this.update(this.value)
}
},
mounted () {
@@ -94,7 +109,7 @@ export default {
// initial SVG size setting
this.resize()
if (this.value === undefined) {
- this.update(this.props.min, 0)
+ this.update(this.min, 0)
} else {
this.update(this.value, 0)
}
@@ -148,7 +163,7 @@ export default {
.startAngle(-this.sizes.angle / 2)
.endAngle(this.sizes.angle / 2)
.cornerRadius(() => {
- return this.props.gstyle === 'rounded' ? this.sizes.gaugeThickness : 0
+ return this.gaugeStyle === 'rounded' ? this.sizes.gaugeThickness : 0
})
const backdrop = this.svg.select('#backdrop')
@@ -188,7 +203,7 @@ export default {
.outerRadius(gaugeR)
.startAngle(-this.sizes.angle / 2)
.cornerRadius(() => {
- return this.props.gstyle === 'rounded' ? this.sizes.gaugeThickness : 0
+ return this.gaugeStyle === 'rounded' ? this.sizes.gaugeThickness : 0
})
const arcTween = function (to) {
@@ -215,17 +230,34 @@ export default {
// update sections
this.updateSegmentArc()
+ const svgPaths = this.svg.select('#sections').selectAll('path')
+ const pathNodes = svgPaths._groups[0]
const segments = this.segments
- this.svg.select('#sections')
- .selectAll('path')
- .data(segments)
- .enter()
- .append('path')
+
+ // check there are more paths than segments
+ if (pathNodes.length !== 0 && pathNodes.length > segments.length) {
+ this.svg.select('#sections')
+ .selectAll('path')
+ .data(segments)
+ .enter()
+ .append('path')
+ // remove any extra paths
+ .filter((_d, i) => {
+ return i > segments.length - 1
+ })
+ .remove()
+ } else {
+ this.svg.select('#sections')
+ .selectAll('path')
+ .data(segments)
+ .enter()
+ .append('path')
+ }
this.svg.select('#sections').selectAll('path')
.attr('d', this.arcs.sections)
.attr('transform', transform)
- .style('fill', (d) => d.color)
+ .style('fill', (d) => d?.color)
// update needle
const needleMask = this.svg.select('#needle-mask')
@@ -247,7 +279,7 @@ export default {
const needle = this.svg.select('#needle')
.style('opacity', () => {
- return this.props.gstyle === 'needle' ? 1 : 0
+ return this.gaugeStyle === 'needle' ? 1 : 0
})
.style('transform', () => {
const x = (this.width / 2) - this.r
@@ -297,7 +329,7 @@ export default {
// in radians
valueToAngle (value) {
const angle = this.sizes.angle
- return angle * (value - this.props.min) / (this.props.max - this.props.min)
+ return angle * (value - this.min) / (this.max - this.min)
},
// in radians
valueToNeedleAngle (value) {
@@ -351,8 +383,8 @@ export default {
},
updateSegmentArc () {
const segments = this.segments
- const minValue = this.props.min
- const maxValue = this.props.max
+ const minValue = this.min
+ const maxValue = this.max
let cAngle = -this.sizes.angle / 2
this.arcs.sections = d3.arc()
@@ -380,7 +412,7 @@ export default {
this.$nextTick(() => {
this.resize()
if (this.value === undefined) {
- this.update(this.props.min, 0)
+ this.update(this.min, 0)
} else {
this.update(this.value, 0)
}
|