From 2f17183bd13f61e2a20d4f60265210e7480d1a4c Mon Sep 17 00:00:00 2001 From: Mark Herwege Date: Fri, 1 Mar 2024 12:48:05 +0100 Subject: [PATCH] Provide meaningful list of units in item-form (#2312) Closes https://github.com/openhab/openhab-core/issues/4082. This PR adds: 1. A curated list of units to show as a drop-down list when creating a Number item with dimension. 2. The possibility to use a different default unit on item creation than the system default unit. 3. The ability to change unit and state description for items. 4. The ability to use the `unitHint` provided by channel types for "Link channel to Item" -> "Create a new Item". By default, the system default unit (for the configured measurement system) will be shown when editing or creating an item. `units.js` contains a number of frequently used units by dimension and measurement system. These will be available in a autosuggest dropdown list. It is still possible to not select from the list and use any other string as a unit. All units for the dimension in `units.js` will be in the dropdown list, but they will be sorted by measurement system. If the measurement system is set to US, imperial units will appear higher in the list. Units that have not explicitely been listed as SI or US will always appear higher. When typing a unit that is not in the curated list, a longer list will be used for autocompletion that considers allowed prefixes to base units and constructs all combinations. `units.js` also contains a field to set a different default unit on item creation than the system default unit. With https://github.com/openhab/openhab-core/pull/4079, the REST API of channel types will provide a unit hint if defined in the binding channel types. If such information is available, this PR adds support for this to be the the suggested unit. If that information is unavailable, the UI will fall back to behavious described above. --------- Also-by: Florian Hotze Signed-off-by: Mark Herwege --- .../org.openhab.ui/web/src/assets/units.js | 180 +++++++++++++++++ .../web/src/components/item/group-form.vue | 132 ++++++++++-- .../web/src/components/item/item-form.vue | 191 +++++++++++++----- .../web/src/components/item/item-mixin.js | 7 +- .../web/src/components/item/uom-mixin.js | 125 +++++++++++- .../web/src/components/thing/channel-list.vue | 12 +- .../org.openhab.ui/web/src/js/store/index.js | 2 + .../src/pages/settings/items/item-edit.vue | 21 +- .../pages/settings/things/link/link-add.vue | 11 +- 9 files changed, 604 insertions(+), 77 deletions(-) create mode 100644 bundles/org.openhab.ui/web/src/assets/units.js diff --git a/bundles/org.openhab.ui/web/src/assets/units.js b/bundles/org.openhab.ui/web/src/assets/units.js new file mode 100644 index 0000000000..1829ac1e12 --- /dev/null +++ b/bundles/org.openhab.ui/web/src/assets/units.js @@ -0,0 +1,180 @@ +// Units defines the possible units for UI unit selection. +// If nothing is defined for an allowed dimension, the UI will fall back on the OH core default unit in the configured measurement system. +// For dimensions defined, any of the fields can be omitted. Logical defaults will be used. +// Units from curated units lists will always be added to the full unit list constructed from baseUnits and prefixes. +// So it is not necessary to explicitly add what is already in the curated units for the full units list. +// However, no prefixes will be applied to these. If you want prefixes to be applied, you should add them in the respective baseUnits Array as well. + +/** + * @typedef Unit + * @property {string} dimension unit dimension (required) + * @property {string[]} [units] units used in a curated shortlist of units, not specific to SI or Imperial measurement system + * @property {string[]} [unitsSI] units used in a curated shortlist of units, specific to the SI measurement system + * @property {string[]} [unitsUS] units used in a curated shortlist of units, specific to the Imperial measurement system + * @property {string} [default] default unit, to be set to override core default unit, not specific to SI or Imperial measurement system + * @property {string} [defaultsSI] default unit in SI measurement system, to be set to override OH core default SI unit + * @property {string} [defaultUS] default unit in Imperial measurement system, to be set to override OH core default Imperial unit + * @property {string[]} [baseUnits] all supported base units that don't allow metric or binary prefixes + * @property {string[]} [baseUnitsMetric] metric base units, the full list of units will include all of these with all metric prefixes + * @property {string[]} [baseUnitsBinary] binary base units, the full list of units will include all of these with all binary prefixes + */ + +/** + * Defines the possible units for UI unit selection. + * @type {Unit[]} + */ +export const Units = [{ + dimension: 'Acceleration', + baseUnits: ['gₙ'], + baseUnitsSI: ['m/s²'] +}, { + dimension: 'AmountOfSubstance', + baseUnits: ['°dH'], + baseUnitsSI: ['mol'] +}, { + dimension: 'Angle', + units: ['°', '\'', '"', 'rad'] +}, { + dimension: 'Area', + unitsSI: ['m²', 'km²', 'ha'], + unitsUS: ['ft²', 'mi²'], + baseUnits: ['ca', 'a', 'in²', 'ac'], + baseUnitsMetric: ['m²'] +}, { + dimension: 'DataAmount', + units: ['bit', 'B', 'kB', 'kiB', 'MB', 'MiB', 'GB', 'GiB', 'TB', 'TiB'], + baseUnitsMetric: ['bit', 'B', 'o'], + baseUnitsBinary: ['bit', 'B', 'o'] +}, { + dimension: 'DataTransferRate', + units: ['bit/s', 'kbit/s', 'Mbit/s', 'Gbit/s'], + baseUnitsMetric: ['bit/s'], + baseUnitsBinary: ['bit/s'] +}, { + dimension: 'Density', + units: ['g/l', 'g/m³', 'kg/m³'], + baseUnits: ['lb/in³'], + baseUnitsMetric: ['g/m³', 'g/mm³', 'g/cm³', 'g/dm³', 'g/ml', 'g/cl', 'g/dl', 'g/l'] +}, { + dimension: 'Dimensionless', + units: ['one', '%', 'dB', 'ppm', 'ppb'], + default: '%' +}, { + dimension: 'ElectricCapcitance', + baseUnitsMetric: ['F'] +}, { + dimension: 'ElectricCharge', + units: ['Ah', 'C'], + baseUnitsMetric: ['C'] +}, { + dimension: 'ElectricConductance', + baseUnitsMetric: ['S'] +}, { + dimension: 'ElectricConductivity', + baseUnitsMetric: ['S/m'] +}, { + dimension: 'ElectricCurrent', + baseUnitsMetric: ['A'] +}, { + dimension: 'ElectricInductance', + baseUnitsMetric: ['H'] +}, { + dimension: 'ElectricPotential', + baseUnitsMetric: ['V'] +}, { + dimension: 'ElectricResistance', + baseUnitsMetric: ['Ω'] +}, { + dimension: 'Energy', + units: ['kWh', 'Wh', 'J', 'kJ', 'cal', 'kcal'], + baseUnitsMetric: ['Ws', 'Wh', 'J', 'cal'] +}, { + dimension: 'Force', + units: ['N', 'kN'], + baseUnitsMetric: ['N'] +}, { + dimension: 'Frequency', + units: ['Hz', 'kHz', 'MHz', 'GHz', 'rpm'], + baseUnitsMetric: ['Hz'] +}, { + dimension: 'Intensity', + units: ['W/m²', 'µW/cm²'], + baseUnitsMetric: ['W/mm²', 'W/cm²', 'W/dm²', 'W/m²'] +}, { + dimension: 'Length', + unitsSI: ['mm', 'cm', 'dm', 'm', 'km'], + unitsUS: ['in', 'ft', 'mi'], + baseUnits: ['yd', 'ch', 'fur', 'lea'] +}, { + dimension: 'LuminousFlux', + baseUnitsMetric: ['lm'] +}, { + dimension: 'LuminousIntensity', + baseUnitsMetric: ['cd'] +}, { + dimension: 'MagneticFlux', + baseUnitsMetric: ['T'] +}, { + dimension: 'Mass', + unitsSI: ['mg', 'g', 'kg', 't'], + baseUnits: ['lb', 'oz', 'st'], + baseUnitsMetric: ['g', 't'] +}, { + dimension: 'Power', + units: ['W', 'kW', 'VA', 'kVA', 'var', 'kvar', 'dBm'], + baseUnits: ['hp', 'kgf', 'lbf'], + baseUnitsMetric: ['W', 'VA', 'var'] +}, { + dimension: 'Pressure', + unitsSI: ['Pa', 'hPa', 'bar', 'mbar', 'mmHg'], + unitsUS: ['inHg', 'psi'], + baseUnits: ['atm'], + baseUnitsMetric: ['Pa', 'bar'] +}, { + dimension: 'RadiationAbsorbedDose', + baseUnitsMetric: ['Gy'] +}, { + dimension: 'RadiationEffectiveDose', + baseUnitsMetric: ['Sv'] +}, { + dimension: 'Radioactivity', + unitsSI: ['Bq', 'Ci'], + baseUnitsMetric: ['Ci'] +}, { + dimension: 'Speed', + unitsSI: ['km/h', 'm/s'], + unitsUS: ['mph', 'in/h'], + baseUnits: ['kn'], + baseUnitsMetric: ['m/s', 'm/h'] +}, { + dimension: 'Temperature', + unitsSI: ['°C', 'K'], + unitsUS: ['°F', 'K'], + baseUnits: ['mired'] +}, { + dimension: 'Time', + units: ['s', 'min', 'h', 'd', 'wk', 'mo', 'y'] +}, { + dimension: 'Volume', + unitsSI: ['ml', 'cl', 'l', 'm³'], + unitsUS: ['gal'], + baseUnits: ['in³', 'ft³'], + baseUnitsMetric: ['l', 'm³'] +}, { + dimension: 'VolumetricFlowRate', + unitsSI: ['l/s', 'l/min', 'm³/s', 'm³/min', 'm³/h', 'm³/d'], + unitsUS: ['gal/min'], + baseUnitsMetric: ['m³/s', 'm³/min', 'm³/h', 'm³/d'] +}] + +/** + * Metric prefixes for metric base units. + * @type {string[]} + */ +export const MetricPrefixes = ['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', 'c', 'd', 'da', 'h', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + +/** + * Binary prefixes for binary base units. + * @type {string[]} + */ +export const BinaryPrefixes = ['ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'] diff --git a/bundles/org.openhab.ui/web/src/components/item/group-form.vue b/bundles/org.openhab.ui/web/src/components/item/group-form.vue index 1d14d4be22..3541fe4bc0 100644 --- a/bundles/org.openhab.ui/web/src/components/item/group-form.vue +++ b/bundles/org.openhab.ui/web/src/components/item/group-form.vue @@ -1,15 +1,15 @@ - @@ -132,8 +132,10 @@ import Item from '@/components/item/item.vue' import * as Types from '@/assets/item-types.js' import ItemMixin from '@/components/item/item-mixin' +import uomMixin from '@/components/item/uom-mixin' + export default { - mixins: [ItemMixin], + mixins: [ItemMixin, uomMixin], components: { ConfigSheet, ItemPicker, @@ -197,9 +199,14 @@ export default { category: (this.channelType) ? this.channelType.category : '', groupNames: [], type: this.channel.itemType || 'Switch', + unit: this.linkUnit(), tags: (defaultTags.find((t) => this.$store.getters.semanticClasses.Points.indexOf(t) >= 0)) ? defaultTags : [...defaultTags, 'Point'] }) }, + linkUnit () { + const dimension = this.channel.itemType.startsWith('Number:') ? this.channel.itemType.split(':')[1] : '' + return dimension ? this.getUnitHint(dimension, this.channelType) : '' + }, loadProfileTypes (channel) { this.ready = false this.selectedChannel = channel