Skip to content

Commit

Permalink
[items] Refactor metadata & itemchannllink APIs (#212)
Browse files Browse the repository at this point in the history
* [items] Refactor ItemChannelLink
* [items] Refactor Metadata
* [items] Export metadata & itemchannellink
* [items] Adjust usage of metadata & itemchannellink
* README: Adjust to metadata & itemchannellink changes
* [items] Use getters instead of accessors
* [items] Remove redundant toString from metadata
* [items] Convert Item channel link configuration to JS object

Also-by: Dan Cunningham <dan@digitaldan.com>
Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
  • Loading branch information
florian-h05 authored Dec 30, 2022
1 parent 9ea4e53 commit eccf2d4
Show file tree
Hide file tree
Showing 20 changed files with 377 additions and 326 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## to be released

| Type | Namespace | Description | Reference | Breaking |
|-------------|------------|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|----------|
| Type | Namespace | Description | Reference | Breaking |
|-------------|-----------|----------------------------------------------------------------------------|--------------------------------------------------------|----------|
| Enhancement | `items` | Refactor `metadata` & `itemchannllink` APIs & add additional functionality | [#212](https://github.com/openhab/openhab-js/pull/212) | **Yes** |

Also see the [Release Milestone](https://github.com/openhab/openhab-js/milestone/11).

Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,9 @@ Calling `getItem(...)` returns an `Item` object with the following properties:
- .isUninitialized ⇒ `boolean`
- .groupNames ⇒ `Array[string]`
- .tags ⇒ `Array[string]`
- .getMetadataValue(namespace) ⇒ `string`
- .updateMetadataValue(namespace, value) ⇒ `string`
- .upsertMetadataValue(namespace, value) ⇒ `boolean`
- .updateMetadataValues(namespaceToValues)
- .getMetadata(namespace) ⇒ `object|null`
- .replaceMetadata(namespace, value, configuration) ⇒ `object`
- .removeMetadata(namespace) ⇒ `object|null`
- .sendCommand(value)
- .sendCommandIfDifferent(value) ⇒ `boolean`
- .postUpdate(value)
Expand Down
1 change: 0 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ module.exports = {
get rules () { return require('./rules'); },
get items () { return require('./items'); },
get things () { return require('./things'); },
get metadata () { return require('./metadata'); },
get triggers () { return require('./triggers'); },
get actions () { return require('./actions'); },
get utils () { return require('./utils'); },
Expand Down
100 changes: 42 additions & 58 deletions items/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
const osgi = require('../osgi');
const utils = require('../utils');
const log = require('../log')('items');
const metadata = require('../metadata/metadata');
const metadata = require('./metadata/metadata');
const ItemHistory = require('./item-history');
const ItemSemantics = require('./item-semantics');
/** @typedef {import('@js-joda/core').ZonedDateTime} time.ZonedDateTime */
Expand Down Expand Up @@ -161,42 +161,32 @@ class Item {
}

/**
* Gets metadata values for this Item.
* @param {string} namespace The namespace for the metadata to retreive
* @returns {string} the metadata associated with this Item and namespace
* Gets metadata with the given name for this Item.
* @param {string} namespace The namespace for the metadata to retrieve
* @returns {{configuration: *, value: string}|null} metadata or null if the Item has no metadata with the given name
*/
getMetadataValue (namespace) {
return metadata.getValue(this.name, namespace);
getMetadata (namespace) {
return metadata.getMetadata(this.name, namespace);
}

/**
* Updates metadata values for this Item.
* @param {string} namespace The namespace for the metadata to update
* @param {string} value the value to update the metadata to
* @returns {string} the updated value
* Updates or adds the given metadata for this Item.
* @param {string} namespace name of the metadata
* @param {string} value value for this metadata
* @param {object} [configuration] optional metadata configuration
* @returns {{configuration: *, value: string}|null} old metadata or `null` if the Item has no metadata with the given name
*/
updateMetadataValue (namespace, value) {
return metadata.updateValue(this.name, namespace, value);
replaceMetadata (namespace, value, configuration) {
return metadata.replaceMetadata(this.name, namespace, value, configuration);
}

/**
* Inserts or updates metadata values for this Item.
* @param {string} namespace The namespace for the metadata to update
* @param {string} value the value to update the metadata to
* @returns {boolean} true iff a new value was inserted
* Removes metadata with a given name from a given Item.
* @param {string} namespace name of the metadata
* @returns {{configuration: *, value: string}|null} removed metadata or `null` if the Item has no metadata with the given name
*/
upsertMetadataValue (namespace, value) {
return metadata.upsertValue(this.name, namespace, value);
}

/**
* Updates metadata values for this Item.
* @param {Map} namespaceToValues A map of namespaces to values to update
*/
updateMetadataValues (namespaceToValues) {
for (const k in namespaceToValues) {
metadata.updateValue(this.name, k, namespaceToValues[k]);
}
removeMetadata (namespace) {
return metadata.removeMetadata(this.name, namespace);
}

/**
Expand Down Expand Up @@ -428,19 +418,19 @@ const addItem = function (itemConfig) {
const namespaceValue = itemConfig.metadata[namespace[i]];
log.debug('addItem: Processing metadata namespace {}', namespace[i]);
if (typeof namespaceValue === 'string') { // namespace as key and it's value as value
metadata.upsertValue(itemConfig.name, namespace[i], namespaceValue);
metadata.replaceMetadata(itemConfig.name, namespace[i], namespaceValue);
} else if (typeof namespaceValue === 'object') { // namespace as key and { value: 'string', configuration: object } as value
metadata.upsertValue(itemConfig.name, namespace[i], namespaceValue.value, namespaceValue.config);
metadata.replaceMetadata(itemConfig.name, namespace[i], namespaceValue.value, namespaceValue.config);
}
}
}

if (itemConfig.type !== 'Group') {
if (typeof itemConfig.channels === 'string') { // single channel link with string
metadata.itemchannellink.upsertItemChannelLink(itemConfig.name, itemConfig.channels);
metadata.itemchannellink.replaceItemChannelLink(itemConfig.name, itemConfig.channels);
} else if (typeof itemConfig.channels === 'object') { // multiple/complex channel links with channel as key and config object as value
const channels = Object.keys(itemConfig.channels);
for (const i in channels) metadata.itemchannellink.upsertItemChannelLink(itemConfig.name, channels[i], itemConfig.channels[channels[i]]);
for (const i in channels) metadata.itemchannellink.replaceItemChannelLink(itemConfig.name, channels[i], itemConfig.channels[channels[i]]);
}
}

Expand All @@ -452,27 +442,28 @@ const addItem = function (itemConfig) {
*
* @memberof items
* @param {String|HostItem} itemOrItemName the Item or the name of the Item to remove
* @returns {boolean} true if the Item was actually removed
* @returns {Item|null} the Item that has been removed or `null` if it has not been removed
*/
const removeItem = function (itemOrItemName) {
let itemName;

if (typeof itemOrItemName === 'string') {
itemName = itemOrItemName;
} else if (itemOrItemName.hasOwnProperty('name')) { // eslint-disable-line no-prototype-builtins
} else if (itemOrItemName instanceof Item) {
itemName = itemOrItemName.name;
} else {
log.warn('Item name is undefined (no Item supplied or supplied name is not a string) so cannot be removed');
return false;
}

let item;
try { // If the Item is not registered, ItemNotFoundException is thrown.
getItem(itemName);
item = getItem(itemName);
} catch (e) {
if (Java.typeName(e.class) === 'org.openhab.core.items.ItemNotFoundException') {
if (e.getClass() === 'org.openhab.core.items.ItemNotFoundException') {
log.error('Item {} not registered so cannot be removed: {}', itemName, e.message);
return false;
} else { // If exception/error is not ItemNotFouncException, rethrow.
return null;
} else { // If exception/error is not ItemNotFoundException, rethrow.
throw Error(e);
}
}
Expand All @@ -482,19 +473,19 @@ const removeItem = function (itemOrItemName) {
try { // If the Item has been successfully removed, ItemNotFoundException is thrown.
itemRegistry.getItem(itemName);
log.warn('Failed to remove Item: {}', itemName);
return false;
return null;
} catch (e) {
if (Java.typeName(e.class) === 'org.openhab.core.items.ItemNotFoundException') {
return true;
if (e.getClass() === 'org.openhab.core.items.ItemNotFoundException') {
return item;
} else { // If exception/error is not ItemNotFoundException, rethrow.
throw Error(e);
}
}
};

/**
* Replaces (upserts) an Item. If an Item exists with the same name, it will be removed and a new Item with
* the supplied parameters will be created in it's place. If an Item does not exist with this name, a new
* Replaces (or adds) an Item. If an Item exists with the same name, it will be removed and a new Item with
* the supplied parameters will be created in its place. If an Item does not exist with this name, a new
* Item will be created with the supplied parameters.
*
* This function can be useful in scripts which create a static set of Items which may need updating either
Expand All @@ -503,25 +494,17 @@ const removeItem = function (itemOrItemName) {
*
* @memberof items
* @param {ItemConfig} itemConfig the Item config describing the Item
* @returns {Item} {@link items.Item}
* @returns {Item|null} the old Item or `null` if it did not exist
* @throws {@link ItemConfig}.name or {@link ItemConfig}.type not set
* @throws failed to create Item
*/
const replaceItem = function (itemConfig) {
try {
const item = getItem(itemConfig.name);
if (typeof item !== 'undefined') {
removeItem(itemConfig.name);
}
} catch (e) {
if (('' + e).startsWith('org.openhab.core.items.ItemNotFoundException')) {
// Item not present
} else {
throw e;
}
const item = getItem(itemConfig.name, true);
if (item !== null) { // Item already existed
removeItem(itemConfig.name);
}

return addItem.apply(this, arguments);
addItem(itemConfig);
return item;
};

/**
Expand Down Expand Up @@ -601,5 +584,6 @@ module.exports = {

throw Error('unsupported function call: ' + name);
}
})
}),
metadata
};
138 changes: 138 additions & 0 deletions items/metadata/itemchannellink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* Item channel link namespace.
* This namespace provides access to Item channel links.
*
* @namespace items.metadata.itemchannellink
*/

const osgi = require('../../osgi');
const utils = require('../../utils');
const log = require('../../log')('itemchannellink');

const ItemChannelLink = Java.type('org.openhab.core.thing.link.ItemChannelLink');
const ChannelUID = Java.type('org.openhab.core.thing.ChannelUID');
const Configuration = Java.type('org.openhab.core.config.core.Configuration');
const managedItemChannelLinkProvider = osgi.getService('org.openhab.core.thing.link.ManagedItemChannelLinkProvider');

/**
* Creates a new ItemChannelLink object.
*
* @private
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @param {object} [conf] channel configuration
* @returns {ItemChannelLink} ItemChannelLink object
*/
const _createItemChannelLink = function (itemName, channelUID, conf) {
log.debug(`Creating ItemChannelLink ${itemName} -> ${channelUID}...`);
if (typeof conf === 'object') {
log.debug(` .. with configuration ${conf}`);
return new ItemChannelLink(itemName, new ChannelUID(channelUID), new Configuration(conf));
} else {
return new ItemChannelLink(itemName, new ChannelUID(channelUID));
}
};

/**
* Gets an ItemChannelLink from the provider.
*
* @memberof items.metadata.itemchannellink
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @returns {{itemName: string, configuration: *, channelUID: string}|null} the ItemChannelLink or `null` if none exists
*/
const getItemChannelLink = function (itemName, channelUID) {
log.debug(`Getting ItemChannelLink ${itemName} -> ${channelUID} from provider...`);
const itemChannelLink = managedItemChannelLinkProvider.get(itemName + ' -> ' + channelUID);
if (itemChannelLink === null || itemChannelLink === undefined) return null;
return {
itemName: itemChannelLink.getItemName().toString(),
channelUID: itemChannelLink.getLinkedUID().toString(),
configuration: utils.javaMapToJsObj(itemChannelLink.getConfiguration().getProperties())
};
};

/**
* Adds a new ItemChannelLink to the provider. Therefore, it adds a channel link to an Item.
*
* @private
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @param {object} [conf] channel configuration
* @returns {{itemName: string, configuration: *, channelUID: string}} the ItemChannelLink
*/
const _addItemChannelLink = function (itemName, channelUID, conf) {
log.debug(`Adding ItemChannelLink ${itemName} -> ${channelUID} to provider...`);
const itemChannelLink = _createItemChannelLink(itemName, channelUID, conf);
managedItemChannelLinkProvider.add(itemChannelLink);
return {
itemName: itemChannelLink.getItemName().toString(),
channelUID: itemChannelLink.getLinkedUID().toString(),
configuration: utils.javaMapToJsObj(itemChannelLink.getConfiguration().getProperties())
};
};

/**
* Update an ItemChannelLink in the provider. Therefore, it updates the channel link of an Item.
*
* @private
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @param {object} [conf] channel configuration
* @returns {{itemName: string, configuration: *, channelUID: string}|null} the old ItemChannelLink or `null` if none exists
*/
const _updateItemChannelLink = function (itemName, channelUID, conf) {
log.debug(`Updating ItemChannelLink ${itemName} -> ${channelUID} in provider...`);
const itemChannelLink = _createItemChannelLink(itemName, channelUID, conf);
managedItemChannelLinkProvider.update(itemChannelLink);
if (itemChannelLink === null || itemChannelLink === undefined) return null;
return {
itemName: itemChannelLink.getItemName().toString(),
channelUID: itemChannelLink.getLinkedUID().toString(),
configuration: utils.javaMapToJsObj(itemChannelLink.getConfiguration().getProperties())
};
};

/**
* Adds or updates an ItemChannelLink.
*
* @memberof items.metadata.itemchannellink
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @param {object} [conf] channel configuration
* @returns {{itemName: string, configuration: *, channelUID: string}|null} the old ItemChannelLink or `null` if it did not exist
*/
const replaceItemChannelLink = function (itemName, channelUID, conf) {
const existing = getItemChannelLink(itemName, channelUID);
if (existing === null) {
_addItemChannelLink(itemName, channelUID, conf);
} else {
_updateItemChannelLink(itemName, channelUID, conf);
}
return existing;
};

/**
* Removes an ItemChannelLink from the provider. Therefore, the channel link is removed from the Item.
*
* @memberof items.metadata.itemchannellink
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @returns {{itemName: string, configuration: *, channelUID: string}|null} the removed ItemChannelLink or `null` if none exists
*/
const removeItemChannelLink = function (itemName, channelUID) {
log.debug(`Removing ItemChannelLink ${itemName} -> ${channelUID} from provider...`);
const itemChannelLink = managedItemChannelLinkProvider.remove(itemName + ' -> ' + channelUID);
if (itemChannelLink === null || itemChannelLink === undefined) return null;
return {
itemName: itemChannelLink.getItemName().toString(),
channelUID: itemChannelLink.getLinkedUID().toString(),
configuration: utils.javaMapToJsObj(itemChannelLink.getConfiguration().getProperties())
};
};

module.exports = {
getItemChannelLink,
replaceItemChannelLink,
removeItemChannelLink
};
Loading

0 comments on commit eccf2d4

Please sign in to comment.