diff --git a/bundles/org.openhab.ui/doc/components/index.md b/bundles/org.openhab.ui/doc/components/index.md index bf2a52892a..ac84314c61 100644 --- a/bundles/org.openhab.ui/doc/components/index.md +++ b/bundles/org.openhab.ui/doc/components/index.md @@ -14,6 +14,7 @@ source: https://github.com/openhab/openhab-webui/edit/main/bundles/org.openhab.u | [`oh-chart`](./oh-chart.html) | [Chart](./oh-chart.html) | Visualize series of data | | [`oh-clock`](./oh-clock.html) | [Digital Clock](./oh-clock.html) | Display a digital clock | | [`oh-colorpicker`](./oh-colorpicker.html) | [Colorpicker](./oh-colorpicker.html) | Control to pick a color | +| [`oh-context`](./oh-context.html) | [Context](./oh-context.html) | Non-rendered component with functions, constants, and scoped variables for widgets | | [`oh-gauge`](./oh-gauge.html) | [Gauge](./oh-gauge.html) | Circular or semi-circular read-only gauge | | [`oh-icon`](./oh-icon.html) | [Icon](./oh-icon.html) | Display an openHAB icon | | [`oh-image`](./oh-image.html) | [Image](./oh-image.html) | Displays an image from a URL or an item | diff --git a/bundles/org.openhab.ui/doc/components/oh-context.md b/bundles/org.openhab.ui/doc/components/oh-context.md new file mode 100644 index 0000000000..bdcffcc2bf --- /dev/null +++ b/bundles/org.openhab.ui/doc/components/oh-context.md @@ -0,0 +1,101 @@ +--- +title: oh-context - Widget Context +component: oh-context +label: Widget Context +description: Non-rendered component with functions, constants, and local variables for widgets +source: https://github.com/openhab/openhab-webui/edit/main/bundles/org.openhab.ui/doc/components/oh-context.md +prev: /docs/ui/components/ +--- + +# oh-context - Widget Context + + + +[[toc]] + + + + +Non-rendered component with functions, constants, and scoped variables for widgets + + +## Configuration + + + +### General +
fn
object.
+ const
object.
+ vars
object and take precedence over variables with the same name from higher contexts.
+ fn
object.'),
+ pt('constants', 'Widget Constants', 'Object with key:constant pairs. Constants are available to expressions in all child components via the const
object.'),
+ pt('variables', 'Widget Variables', 'Object with key:variable default value pairs. Variables are available to expressions in all child components via the vars
object and take precedence over variables with the same name from higher contexts.')
+]
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/widgets/system/index.js b/bundles/org.openhab.ui/web/src/assets/definitions/widgets/system/index.js
index c41fdb64e2..d53d953e61 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/widgets/system/index.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/widgets/system/index.js
@@ -20,6 +20,10 @@ import ColorpickerParameters from './colorpicker.js'
export const OhColorpickerDefinition = () => new WidgetDefinition('oh-colorpicker', 'Colorpicker', 'Control to pick a color')
.params(ColorpickerParameters())
+import ContextParameters from './context.js'
+export const OhContextDefinition = () => new WidgetDefinition('oh-context', 'Context', 'Non-rendered component with functions, constants, and scoped variables for widgets')
+ .params(ContextParameters())
+
import GaugeParameters from './gauge.js'
export const OhGaugeDefinition = () => new WidgetDefinition('oh-gauge', 'Gauge', 'Circular or semi-circular read-only gauge')
.params(GaugeParameters())
diff --git a/bundles/org.openhab.ui/web/src/components/config/editor/hint-components.js b/bundles/org.openhab.ui/web/src/components/config/editor/hint-components.js
index 3a7ec25757..df78ef7799 100644
--- a/bundles/org.openhab.ui/web/src/components/config/editor/hint-components.js
+++ b/bundles/org.openhab.ui/web/src/components/config/editor/hint-components.js
@@ -107,6 +107,8 @@ function hintExpression (cm, line) {
{ text: 'props.', displayText: 'props', description: 'Access to the props of the parent root component' },
{ text: 'config.', displayText: 'config', description: 'Access to the configuration of the current component' },
{ text: 'vars.', displayText: 'vars', description: 'Access to context vars' },
+ { text: 'fn.', displayText: 'fn', description: 'Access to oh-context functions' },
+ { text: 'const.', displayText: 'const', description: 'Access to oh-context constants' },
{ text: 'loop.', displayText: 'loop', description: 'Access to oh-repeater loop variables' },
{ text: 'JSON.', displayText: 'JSON', description: 'Access to the JSON object functions' },
{ text: 'Math.', displayText: 'Math', description: 'Access to the Math object functions' },
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/modals/modal-mixin.js b/bundles/org.openhab.ui/web/src/components/widgets/modals/modal-mixin.js
index 912e6e246e..d80f705df4 100644
--- a/bundles/org.openhab.ui/web/src/components/widgets/modals/modal-mixin.js
+++ b/bundles/org.openhab.ui/web/src/components/widgets/modals/modal-mixin.js
@@ -12,6 +12,7 @@ export default {
return {
currentTab: 0,
vars: {},
+ ctxVars: {},
tabVars: {}
}
},
@@ -24,6 +25,7 @@ export default {
store: this.$store.getters.trackedItems,
props: this.modalConfig,
vars: this.vars,
+ ctxVars: this.ctxVars,
modalConfig: this.modalConfig // For configuration of oh- components
}
},
@@ -67,6 +69,7 @@ export default {
onTabChange (idx) {
this.currentTab = idx
this.$set(this, 'vars', {})
+ this.$set(this, 'ctxVars', {})
},
tabContext (tab) {
const page = this.$store.getters.page(tab.config.page.replace('page:', ''))
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/system/index.js b/bundles/org.openhab.ui/web/src/components/widgets/system/index.js
index 10145440f5..6e60f5d461 100644
--- a/bundles/org.openhab.ui/web/src/components/widgets/system/index.js
+++ b/bundles/org.openhab.ui/web/src/components/widgets/system/index.js
@@ -22,3 +22,4 @@ export { default as OhRepeater } from './oh-repeater.vue'
export { default as OhChart } from './oh-chart.vue'
export { default as OhClock } from './oh-clock.vue'
export { default as OhSipclient } from './oh-sipclient.vue'
+export { default as OhContext } from './oh-context.vue'
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/system/oh-button.vue b/bundles/org.openhab.ui/web/src/components/widgets/system/oh-button.vue
index fa633349cf..ffa8543063 100644
--- a/bundles/org.openhab.ui/web/src/components/widgets/system/oh-button.vue
+++ b/bundles/org.openhab.ui/web/src/components/widgets/system/oh-button.vue
@@ -22,19 +22,29 @@ export default {
}
if (this.config.clearVariable && !this.config.clearVariableKey) {
if (Array.isArray(this.config.clearVariable)) {
- this.config.clearVariable.forEach((v) => this.$set(this.context.vars, v, undefined))
+ this.config.clearVariable.forEach((v) => {
+ const clearVariableScope = this.getVariableScope(this.context.ctxVars, this.context.varScope, v)
+ const clearVariableLocation = (clearVariableScope) ? this.context.ctxVars[clearVariableScope] : this.context.vars
+ this.$set(clearVariableLocation, v, undefined)
+ })
} else if (typeof this.config.clearVariable === 'string') {
- this.$set(this.context.vars, this.config.clearVariable, undefined)
+ const clearVariableScope = this.getVariableScope(this.context.ctxVars, this.context.varScope, this.config.clearVariable)
+ const clearVariableLocation = (clearVariableScope) ? this.context.ctxVars[clearVariableScope] : this.context.vars
+ this.$set(clearVariableLocation, this.config.clearVariable, undefined)
}
}
if (this.config.clearVariable && this.config.clearVariableKey) {
let value = this.context.vars[this.config.clearVariable]
if (Array.isArray(this.config.clearVariableKey)) {
this.config.clearVariableKey.forEach((key) => {
- value = this.setVariableKeyValues(value, key, undefined)
+ const clearVariableScope = this.getVariableScope(this.context.ctxVars, this.context.varScope, this.config.clearVariable)
+ const clearVariableLocation = (clearVariableScope) ? this.context.ctxVars[clearVariableScope] : this.context.vars
+ value = this.setVariableKeyValues(clearVariableLocation, key, undefined)
})
} else if (typeof this.config.clearVariableKey === 'string') {
- value = this.setVariableKeyValues(value, this.config.clearVariableKey, undefined)
+ const clearVariableScope = this.getVariableScope(this.context.ctxVars, this.context.varScope, this.config.clearVariable)
+ const clearVariableLocation = (clearVariableScope) ? this.context.ctxVars[clearVariableScope] : this.context.vars
+ value = this.setVariableKeyValues(clearVariableLocation, this.config.clearVariableKey, undefined)
}
this.$set(this.context.vars, this.config.clearVariable, value)
}
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/system/oh-context.vue b/bundles/org.openhab.ui/web/src/components/widgets/system/oh-context.vue
new file mode 100644
index 0000000000..80631ebb4c
--- /dev/null
+++ b/bundles/org.openhab.ui/web/src/components/widgets/system/oh-context.vue
@@ -0,0 +1,89 @@
+
+ null
is returned.
+ *
+ * oh-context variables are local in scope to the oh-context and it's children and take precedence over other variables
+ * of the same name from higher contexts/scopes, including normal variables.
+ * Changes to oh-context variables done by children are always propagated to the parent, which is not the case with normal variables used inside widgets.
+ *
+ * @param {object} varObj the object containing the variables for each context/scope
+ * @param {string} scopeObj the key of the given variable context/scope
+ * @param {string} key the key of the variable
+ * @returns {string|null} the key of the variable context/scope to be used
+ */
+ getVariableScope (varObj, scopeObj, key) {
+ if (!scopeObj) return null
+ const scopeIDs = scopeObj.split('-')
+ for (let scope_idx = scopeIDs.length; scope_idx > 1; scope_idx--) {
+ const scopeKey = scopeIDs.slice(0, scope_idx).join('-')
+ if (Object.keys(varObj[scopeKey]).includes(key)) return scopeKey
+ }
+ return null
}
}
}
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/widget-actions.js b/bundles/org.openhab.ui/web/src/components/widgets/widget-actions.js
index 1baa8a6278..b281d61405 100644
--- a/bundles/org.openhab.ui/web/src/components/widgets/widget-actions.js
+++ b/bundles/org.openhab.ui/web/src/components/widgets/widget-actions.js
@@ -271,10 +271,12 @@ export const actionsMixin = {
const actionVariable = actionConfig[prefix + 'actionVariable']
let actionVariableValue = actionConfig[prefix + 'actionVariableValue']
const actionVariableKey = actionConfig[prefix + 'actionVariableKey']
+ const actionVariableScope = this.getVariableScope(context.ctxVars, context.varScope, actionVariable)
+ const actionVariableLocation = (actionVariableScope) ? context.ctxVars[actionVariableScope] : context.vars
if (actionVariableKey) {
- actionVariableValue = this.setVariableKeyValues(context.vars[actionVariable], actionVariableKey, actionVariableValue)
+ actionVariableValue = this.setVariableKeyValues(actionVariableLocation[actionVariable], actionVariableKey, actionVariableValue)
}
- this.$set(context.vars, actionVariable, actionVariableValue)
+ this.$set(actionVariableLocation, actionVariable, actionVariableValue)
break
default:
console.log('Invalid action: ' + action)
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/widget-expression-mixin.js b/bundles/org.openhab.ui/web/src/components/widgets/widget-expression-mixin.js
index 273a3664a0..7dd513ba05 100644
--- a/bundles/org.openhab.ui/web/src/components/widgets/widget-expression-mixin.js
+++ b/bundles/org.openhab.ui/web/src/components/widgets/widget-expression-mixin.js
@@ -68,7 +68,9 @@ export default {
items: ctx.store,
props: props || this.props,
config: ctx.component.config,
- vars: ctx.vars,
+ fn: ctx.fn,
+ const: ctx.const,
+ vars: this.getAllVars(ctx),
loop: ctx.loop,
Math: Math,
Number: Number,
@@ -114,6 +116,24 @@ export default {
viewAreaWidth: pageContent.clientWidth - parseFloat(pageContentStyle.paddingLeft) - parseFloat(pageContentStyle.paddingRight),
viewAreaHeight: pageContent.clientHeight - parseFloat(pageContentStyle.paddingTop) - parseFloat(pageContentStyle.paddingBottom)
}
+ },
+ getAllVars (context) {
+ const vars = {}
+ if (context.vars) {
+ for (const varKey in context.vars) {
+ vars[varKey] = context.vars[varKey]
+ }
+ }
+ if (context.varScope) {
+ const scopeIDs = context.varScope.split('-')
+ for (let scope_idx = 1; scope_idx < scopeIDs.length; scope_idx++) {
+ let scopeKey = scopeIDs.slice(0, scope_idx + 1).join('-')
+ for (const varKey in context.ctxVars[scopeKey]) {
+ vars[varKey] = context.ctxVars[scopeKey][varKey]
+ }
+ }
+ }
+ return vars
}
}
}
diff --git a/bundles/org.openhab.ui/web/src/components/widgets/widget-mixin.js b/bundles/org.openhab.ui/web/src/components/widgets/widget-mixin.js
index b42cfd76cd..b40c9b38dc 100644
--- a/bundles/org.openhab.ui/web/src/components/widgets/widget-mixin.js
+++ b/bundles/org.openhab.ui/web/src/components/widgets/widget-mixin.js
@@ -9,6 +9,7 @@ export default {
data () {
return {
vars: (this.context) ? this.context.vars : {},
+ ctxVars: (this.context) ? this.context.ctxVars : {},
widgetVars: {}
}
},
@@ -32,7 +33,7 @@ export default {
if (sourceConfig) {
if (typeof sourceConfig !== 'object') return {}
for (const key in sourceConfig) {
- if (key === 'visible' || key === 'visibleTo' || key === 'stylesheet') continue
+ if (key === 'visible' || key === 'visibleTo' || key === 'stylesheet' || key === 'constants') continue
this.$set(evalConfig, key, this.evaluateExpression(key, sourceConfig[key]))
}
}
@@ -69,7 +70,7 @@ export default {
}
},
mounted () {
- if (this.context && this.context.component.config && this.context.component.config.stylesheet) {
+ if (this.context && this.context.component && this.context.component.config && this.context.component.config.stylesheet) {
this.cssUid = 'scoped-' + this.$f7.utils.id()
this.$el.classList.add(this.cssUid)
@@ -92,7 +93,11 @@ export default {
component: component,
rootcomponent: this.context.root || this.context.component,
props: this.props,
+ fn: this.context.fn,
+ const: this.context.const,
vars: this.context.vars,
+ varScope: this.varScope || this.context.varScope,
+ ctxVars: this.context.ctxVars,
loop: this.context.loop,
store: this.context.store,
config: this.context.config,
@@ -117,7 +122,11 @@ export default {
component: widget,
root: widget,
props: this.config,
+ fn: this.context.fn,
+ const: this.context.const,
vars: this.widgetVars,
+ varScope: this.varScope || this.context.varScope,
+ ctxVars: this.context.ctxVars,
store: this.context.store,
config: this.context.config,
editmode: this.context.editmode,
diff --git a/bundles/org.openhab.ui/web/src/pages/developer/widgets/widget-edit.vue b/bundles/org.openhab.ui/web/src/pages/developer/widgets/widget-edit.vue
index 5cd1de0f4f..18a6d8d6bc 100644
--- a/bundles/org.openhab.ui/web/src/pages/developer/widgets/widget-edit.vue
+++ b/bundles/org.openhab.ui/web/src/pages/developer/widgets/widget-edit.vue
@@ -124,6 +124,7 @@ export default {
split: 'vertical',
props: {},
vars: {},
+ ctxVars: {},
blockKey: this.$f7.utils.id(),
widgetKey: this.$f7.utils.id(),
widgetPropsOpened: false,
@@ -146,7 +147,8 @@ export default {
: this.widget,
store: this.$store.getters.trackedItems,
props: this.props,
- vars: this.vars
+ vars: this.vars,
+ ctxVars: this.ctxVars
}
},
widget () {
@@ -301,6 +303,7 @@ export default {
this.$store.dispatch('sendCommand', { itemName, cmd })
},
redrawWidget () {
+ this.ctxVars = {}
this.widgetKey = this.$f7.utils.id()
// const wd = this.widgetDefinition
// this.widgetDefinition = 'component: Label\nnconfig: { text: "Redrawing..."}'