const osgi = require('../osgi');
const utils = require('../utils');
const environment = require('../environment');
const log = require('../log')('itemchannellink');
const { _getItemName } = require('../helpers');
const itemChannelLinkRegistry = environment.useProviderRegistries()
? require('@runtime/provider').itemChannelLinkRegistry
: osgi.getService('org.openhab.core.thing.link.ItemChannelLinkRegistry');
const JavaItemChannelLink = 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');
/**
* Item channel link namespace.
* This namespace provides access to Item channel links.
*
* @namespace items.itemChannelLink
*/
/**
* @typedef {import('./items').Item} Item
* @private
*/
/**
* Class representing an openHAB Item -> channel link.
*
* @memberof items.itemChannelLink
* @hideconstructor
*/
class ItemChannelLink {
itemName;
channelUID;
configuration;
/**
* @param {*} rawItemChannelLink {@link https://www.openhab.org/javadoc/latest/org/openhab/core/thing/link/itemchannellink org.openhab.core.thing.link.ItemChannelLink}
*/
constructor (rawItemChannelLink) {
this.itemName = rawItemChannelLink.getItemName().toString();
this.channelUID = rawItemChannelLink.getLinkedUID().toString();
this.configuration = utils.javaMapToJsObj(rawItemChannelLink.getConfiguration().getProperties());
}
}
/**
* Gets a channel link of from an Item.
*
* @memberof items.itemChannelLink
* @param {Item|string} itemOrName {@link Item} or the name of the Item
* @param {string} channelUID
* @returns {ItemChannelLink|null} the ItemChannelLink or `null` if none exists
*/
function getItemChannelLink (itemOrName, channelUID) {
const itemName = _getItemName(itemOrName);
log.debug(`Getting ItemChannelLink ${itemName} -> ${channelUID} from registry.`);
const itemChannelLink = itemChannelLinkRegistry.get(itemName + ' -> ' + channelUID);
if (itemChannelLink === null) return null;
return new ItemChannelLink(itemChannelLink);
}
/**
* Creates a new ItemChannelLink object.
* This ItemChannelLink is not registered with any provider and therefore cannot be accessed.
*
* @private
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @param {object} [conf] channel configuration
* @returns {JavaItemChannelLink} ItemChannelLink object
*/
function _createItemChannelLink (itemName, channelUID, conf) {
log.debug(`Creating ItemChannelLink ${itemName} -> ${channelUID}`);
if (typeof conf === 'object') {
log.debug(` with configuration: ${JSON.stringify(conf)}`);
return new JavaItemChannelLink(itemName, new ChannelUID(channelUID), new Configuration(conf));
} else {
return new JavaItemChannelLink(itemName, new ChannelUID(channelUID));
}
}
/**
* Adds a new channel link to an Item.
*
* If this is called from file-based scripts, the Item -> channel link is registered with the ScriptedItemChannelLinkProvider and shares the same lifecycle as the script.
* You can still persist the Item -> channel link permanently in this case by setting the `persist` parameter to `true`.
* If this is called from UI-based scripts, the Item -> channel link is stored to the ManagedItemChannelLinkProvider and independent of the script's lifecycle.
*
* @memberOf items.itemChannelLink
* @param {Item|string} itemOrName {@link Item} or the name of the Item
* @param {string} channelUID
* @param {object} [configuration] channel configuration
* @param {boolean} [persist=false] whether to persist the Item -> channel link permanently (only respected for file-based scripts)
* @returns {ItemChannelLink} the ItemChannelLink
* @throws {Error} if the Item -> channel link already exists
*/
function addItemChannelLink (itemOrName, channelUID, configuration, persist = false) {
const itemName = _getItemName(itemOrName);
log.debug(`Adding ItemChannelLink ${itemName} -> ${channelUID} to registry...`);
let itemChannelLink = _createItemChannelLink(itemName, channelUID, configuration);
try {
itemChannelLink = (persist && environment.useProviderRegistries()) ? itemChannelLinkRegistry.addPermanent(itemChannelLink) : itemChannelLinkRegistry.add(itemChannelLink);
} catch (e) {
if (e instanceof Java.type('java.lang.IllegalArgumentException')) {
throw new Error(`Cannot add ItemChannelLink ${itemName} -> ${channelUID}: already exists`);
} else {
throw e; // re-throw other errors
}
}
return new ItemChannelLink(itemChannelLink);
}
/**
* Updates a channel link of an Item.
*
* @private
* @param {string} itemName the name of the Item
* @param {string} channelUID
* @param {object} [configuration] channel configuration
* @returns {ItemChannelLink|null} the old ItemChannelLink or `null` if none exists
*/
function _updateItemChannelLink (itemName, channelUID, configuration) {
log.debug(`Updating ItemChannelLink ${itemName} -> ${channelUID} in registry...`);
let itemChannelLink = _createItemChannelLink(itemName, channelUID, configuration);
itemChannelLink = itemChannelLinkRegistry.update(itemChannelLink);
if (itemChannelLink === null) return null;
return new ItemChannelLink(itemChannelLink);
}
/**
* Adds or updates a channel link of an Item.
* If you use this in file-based scripts, better use {@link addItemChannelLink} to provide channel links.
*
* If an Item -> channel link is not provided by this script or the ManagedItemChannelLinkProvider, it is not editable and a warning is logged.
*
* @memberof items.itemChannelLink
* @param {Item|string} itemOrName {@link Item} or the name of the Item
* @param {string} channelUID
* @param {object} [configuration] channel configuration
* @returns {ItemChannelLink|null} the old ItemChannelLink or `null` if it did not exist
*/
function replaceItemChannelLink (itemOrName, channelUID, configuration) {
const itemName = _getItemName(itemOrName);
const itemChannelLink = getItemChannelLink(itemName, channelUID);
return (itemChannelLink === null) ? addItemChannelLink(itemName, channelUID, configuration) : _updateItemChannelLink(itemName, channelUID, configuration);
}
/**
* Removes a channel link from an Item.
*
* @memberof items.itemChannelLink
* @param {Item|string} itemOrName {@link Item} or the name of the Item
* @param {string} channelUID
* @returns {ItemChannelLink|null} the removed ItemChannelLink or `null` if none exists, or it cannot be removed
*/
function removeItemChannelLink (itemOrName, channelUID) {
const itemName = _getItemName(itemOrName);
log.debug(`Removing ItemChannelLink ${itemName} -> ${channelUID} from registry...`);
const itemChannelLink = itemChannelLinkRegistry.remove(itemName + ' -> ' + channelUID);
if (itemChannelLink === null) return null;
return new ItemChannelLink(itemChannelLink);
}
/**
* Removes all channel links from the given Item.
*
* @memberof items.itemChannelLink
* @param {string} itemName the name of the Item
* @returns {number} number of links removed
*/
function removeLinksForItem (itemName) {
return itemChannelLinkRegistry.removeLinksForItem(itemName);
}
/**
* Removes all orphaned (Item or channel missing) links.
*
* @memberof items.itemChannelLink
* @returns {number} number of links removed
*/
function removeOrphanedItemChannelLinks () {
return itemChannelLinkRegistry.purge();
}
module.exports = {
getItemChannelLink,
addItemChannelLink,
replaceItemChannelLink,
removeItemChannelLink,
removeLinksForItem,
removeOrphanedItemChannelLinks,
ItemChannelLink
};