diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/deviceattributes.js b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/deviceattributes.js
index 9e097ad84..7df9dca9d 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/deviceattributes.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/deviceattributes.js
@@ -48,7 +48,7 @@ export default {
parameters: (itemType, item) => [
p.inverted(itemType === 'Rollershutter'),
p.presets(item.stateDescription, '20=Morning,60=Afternoon,80=Evening:@Setting.Night'),
- p.language(item.settings && item.settings.regional.language),
+ p.language(item.settings?.regional?.language),
p.actionMappings({ default: 'value' }, 'Close=0,Open=100,Lower=0,Raise=100', (config) => {
const primaryControl = getGroupParameter('primaryControl', item.groups) || 'position'
if (itemType === 'Dimmer') {
@@ -73,7 +73,7 @@ export default {
parameters: (itemType, item) => [
p.inverted(itemType === 'Rollershutter'),
p.presets(item.stateDescription, '20=Morning,60=Afternoon,80=Evening:@Setting.Night'),
- p.language(item.settings && item.settings.regional.language),
+ p.language(item.settings?.regional?.language),
...(getGroupParameter('primaryControl', item.groups) !== 'tilt' ? [] : [
p.actionMappings({ default: 'value' }, 'Close=0,Open=100', (config) => {
if (itemType === 'Dimmer') {
@@ -107,7 +107,7 @@ export default {
itemTypes: ['Number', 'String'],
parameters: (itemType, item) => [
p.supportedInputs(item.stateDescription, itemType === 'String' ? 'HDMI1=Cable,HDMI2=Kodi' : '1=Cable,2=Kodi'),
- p.language(item.settings && item.settings.regional.language),
+ p.language(item.settings?.regional?.language),
p.retrievable()
]
},
@@ -410,7 +410,7 @@ export default {
p.retrievable(),
p.supportedModes(item.stateDescription),
p.ordered(),
- p.language(item.settings && item.settings.regional.language),
+ p.language(item.settings?.regional?.language),
p.actionMappings(
{ set: 'mode', ...(config.ordered && { adjust: '(±deltaValue)' }) },
'Close=Down,Open=Up,Lower=Down,Raise=Up'
@@ -421,7 +421,7 @@ export default {
RangeValue: {
itemTypes: ['Dimmer', 'Number', 'Number:*', 'Rollershutter'],
supports: ['multiInstance'],
- parameters: (itemType, item) => [
+ parameters: (itemType, item, config) => [
p.capabilityNames(item.groups.length ? item.label : '@Setting.RangeValue', '@Setting.FanSpeed,Speed'),
p.inverted(itemType === 'Rollershutter'),
p.nonControllable(item.stateDescription),
@@ -432,12 +432,17 @@ export default {
? [p.supportedCommands(['UP', 'DOWN', 'MOVE', 'STOP'], 'UP=@Value.Open,DOWN=@Value.Close,STOP=@Value.Stop')]
: []),
p.supportedRange(
- item.stateDescription,
- itemType === 'Dimmer' || itemType === 'Rollershutter' ? '0:100:1' : '0:10:1'
+ item,
+ config,
+ itemType === 'Dimmer' || itemType === 'Rollershutter'
+ ? '0:100:1'
+ : config.nonControllable
+ ? '0:10:0.01'
+ : '0:10:1'
),
p.presets(item.stateDescription, '1=@Value.Low:Lowest,10=@Value.High:Highest'),
p.unitOfMeasure(item),
- p.language(item.settings && item.settings.regional.language),
+ p.language(item.settings?.regional?.language),
p.actionMappings({ set: 'value', adjust: '(±deltaValue)' }, 'Close=0,Open=100,Lower=(-10),Raise=(+10)'),
p.stateMappings({ default: 'value', range: 'minValue:maxValue' }, 'Closed=0,Open=1:100')
]
@@ -452,7 +457,7 @@ export default {
: [p.inverted()]),
p.nonControllable(item.stateDescription),
p.retrievable(),
- p.language(item.settings && item.settings.regional.language),
+ p.language(item.settings?.regional?.language),
p.actionMappings(['ON', 'OFF'], 'Close=OFF,Open=ON'),
p.stateMappings(['ON', 'OFF'], 'Closed=OFF,Open=ON')
]
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/devicetypes.js b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/devicetypes.js
index 8bd93d8a8..1f93bcb1b 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/devicetypes.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/devicetypes.js
@@ -60,7 +60,7 @@ const thermostatAttributes = [
const blindParameters = (_, item) => {
const attributes = ['PositionState', 'TiltAngle']
- const metadata = item.members.map((mbr) => mbr.metadata && mbr.metadata.alexa.value).join(',')
+ const metadata = item.members.map((mbr) => mbr.metadata?.alexa?.value).filter(Boolean).join(',')
return attributes.every((attr) => metadata.includes(attr)) ? [p.primaryControl()] : []
}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/helpers.js b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/helpers.js
index 1a69d77cc..119e3d865 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/helpers.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/helpers.js
@@ -19,7 +19,7 @@ export const docLink = (title, anchor) => {
return `${title}`
}
-export const getGroupParameter = (parameter, groups) => {
+export const getGroupParameter = (parameter, groups = []) => {
for (const group of groups) {
const config = group.metadata.alexa.config || {}
if (parameter in config) return config[parameter]
@@ -39,25 +39,34 @@ export const getSemanticFormat = (type, format) =>
''
)
+export const getSupportedRange = (item, config, defaultValue) => {
+ const { minimum, maximum, step, pattern } = item.stateDescription || {}
+ if (!isNaN(minimum) && !isNaN(maximum) && !isNaN(step)) return `${minimum}:${maximum}:${step}`
+ const itemType = item.groupType || item.type
+ if (itemType.startsWith('Number') && config.nonControllable) {
+ const { precision, specifier } = pattern?.match(/%\d*(?:\.(?\d+))?(?[df])/)?.groups || {}
+ const [minimum, maximum] = defaultValue.split(':', 2)
+ if (specifier === 'd') return `${minimum}:${maximum}:1`
+ if (precision <= 16) return `${minimum}:${maximum}:${1 / 10 ** precision}`
+ }
+ return defaultValue
+}
+
export const getTemperatureScale = (item) => {
const itemType = item.groupType || item.type
- const unitSymbol = item.unitSymbol
- const statePresentation = (item.stateDescription && item.stateDescription.pattern) || ''
- const format = (itemType === 'Number:Temperature' && unitSymbol) || statePresentation
- if (format.endsWith('°C')) return 'CELSIUS'
- if (format.endsWith('°F')) return 'FAHRENHEIT'
- const { measurementSystem } = (item.settings && item.settings.regional) || {}
+ const format = (itemType === 'Number:Temperature' && item.unitSymbol) || item.stateDescription?.pattern
+ if (format?.endsWith('°C')) return 'CELSIUS'
+ if (format?.endsWith('°F')) return 'FAHRENHEIT'
+ const measurementSystem = item.settings?.regional?.measurementSystem
if (measurementSystem === 'SI') return 'CELSIUS'
if (measurementSystem === 'US') return 'FAHRENHEIT'
}
export const getUnitOfMeasure = (item) => {
const itemType = item.groupType || item.type
- const unitSymbol = item.unitSymbol
- const statePresentation = (item.stateDescription && item.stateDescription.pattern) || ''
const format =
((itemType === 'Dimmer' || itemType === 'Rollershutter') && '%') ||
- (itemType.startsWith('Number:') && unitSymbol) ||
- statePresentation
- return Object.keys(UNITS_OF_MEASURE).find((id) => format.endsWith(UNITS_OF_MEASURE[id]))
+ (itemType.startsWith('Number:') && item.unitSymbol) ||
+ item.stateDescription?.pattern
+ return Object.keys(UNITS_OF_MEASURE).find((id) => format?.endsWith(UNITS_OF_MEASURE[id]))
}
diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/parameters.js b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/parameters.js
index cfbfe10b0..5153406f1 100644
--- a/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/parameters.js
+++ b/bundles/org.openhab.ui/web/src/assets/definitions/metadata/alexa/parameters.js
@@ -13,6 +13,7 @@ import {
getGroupParameter,
getOptions,
getSemanticFormat,
+ getSupportedRange,
getTemperatureScale,
getUnitOfMeasure,
titleCase
@@ -202,7 +203,7 @@ export default {
name: 'nonControllable',
label: 'Non-Controllable',
type: 'BOOLEAN',
- default: (stateDescription && stateDescription.readOnly) === true,
+ default: stateDescription?.readOnly === true,
visible: (_, config) => !!config.retrievable
}),
ordered: () => ({
@@ -229,14 +230,12 @@ export default {
` (${docLink('Asset Catalog')})`,
type: 'TEXT',
default:
- stateDescription &&
- stateDescription.options &&
- stateDescription.options
- .filter((option) => !isNaN(option.value))
+ stateDescription?.options?.filter((option) => !isNaN(option.value))
.map((option) => `${option.value}=${option.label}`)
.slice(0, STATE_DESCRIPTION_OPTIONS_LIMIT),
placeholder: placeholder.replace(/,/g, '\n'),
- multiple: true
+ multiple: true,
+ visible: (_, config) => !config.nonControllable
}),
primaryControl: () => ({
name: 'primaryControl',
@@ -356,10 +355,7 @@ export default {
description: 'Each input formatted as inputValue=inputName1:inputName2:...
',
type: 'TEXT',
default:
- stateDescription &&
- stateDescription.options &&
- stateDescription.options
- .map((option) => `${option.value}=${option.label}`)
+ stateDescription?.options?.map((option) => `${option.value}=${option.label}`)
.slice(0, STATE_DESCRIPTION_OPTIONS_LIMIT),
placeholder: placeholder.replace(/,/g, '\n'),
multiple: true,
@@ -372,10 +368,7 @@ export default {
`Each mode formatted as mode=@assetIdOrName1:@assetIdOrName2:...
(${docLink('Asset Catalog')})`,
type: 'TEXT',
default:
- stateDescription &&
- stateDescription.options &&
- stateDescription.options
- .map((option) => `${option.value}=${option.label}`)
+ stateDescription?.options?.map((option) => `${option.value}=${option.label}`)
.slice(0, STATE_DESCRIPTION_OPTIONS_LIMIT),
placeholder: 'Normal=Normal:Cottons\nWhites=Whites',
multiple: true,
@@ -391,18 +384,12 @@ export default {
multiple: true,
advanced: true
}),
- supportedRange: (stateDescription, defaultValue) => ({
+ supportedRange: (item, config, defaultValue) => ({
name: 'supportedRange',
label: 'Supported Range',
description: 'Formatted as minValue:maxValue:precision
',
type: 'TEXT',
- default:
- stateDescription &&
- !isNaN(stateDescription.minimum) &&
- !isNaN(stateDescription.maximum) &&
- !isNaN(stateDescription.step)
- ? `${stateDescription.minimum}:${stateDescription.maximum}:${stateDescription.step}`
- : defaultValue,
+ default: getSupportedRange(item, config, defaultValue),
pattern: '[+-]?[0-9]+:[+-]?[0-9]+:[0-9]+'
}),
supportedThermostatModes: () => ({