-
-
Notifications
You must be signed in to change notification settings - Fork 38
[items] Add support for providing Items, metadata & channel links from file-based scripts #441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
b8d277b
[environment] Initial contribution
florian-h05 7217ca8
[items] Add support for providing metadata from scripts
florian-h05 e0b41b0
[items] Add support for providing Item -> channel links from scripts
florian-h05 69f9c8e
[environment] Add method to decide whether to use provider registries
florian-h05 1f47c4a
[items] Refactor removeItem method
florian-h05 6c89435
[items] Add support for providing Items from scripts
florian-h05 7a602e7
[items] Remove isEditable check
florian-h05 319eaae
Move metadata.js & itemchannellink.js to root items dir
florian-h05 1153890
Update type defs
florian-h05 589aa78
Update JSDoc
florian-h05 00f8c06
Update CHANGELOG
florian-h05 9879c1c
Code & JSDoc improvements
florian-h05 154d88c
Update README
florian-h05 8cf77d7
Code improvement
florian-h05 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // This module MUST NOT depend on any other library code to avoid circular dependencies | ||
|
|
||
| /** | ||
| * Environment namespace. | ||
| * This namespace handles utilities for determining the script environment and retrieving information about it. | ||
| * | ||
| * @namespace environment | ||
| */ | ||
|
|
||
| /** | ||
| * Returns whether the code is running from a file-based script. | ||
| * This is determined by checking if the `javax.script.filename` global variable is defined. | ||
| * This is useful to distinguish between file-based scripts and UI-based scripts in openHAB. | ||
| * | ||
| * @memberOf environment | ||
| * @return {boolean} true if the script is file-based, false otherwise | ||
| */ | ||
| function isFileBasedScript () { | ||
| return globalThis['javax.script.filename'] !== undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the host openHAB version supports providing openHAB entities via the `@runtime/provider` module. | ||
| * | ||
| * @private | ||
| * @return {boolean} true if the provider module is available, false otherwise | ||
| */ | ||
| function _hasProviderSupport () { | ||
| return !!require('@runtime/provider').itemRegistry; | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the registry implementations from the `@runtime/provider` module should be used instead of the default ones from the `@runtime` module. | ||
| * Provider implementations should be used if the host openHAB version supports it and the script is running from a file-based script. | ||
| * | ||
| * @memberOf environment | ||
| * @return {boolean} true if the provider registry implementations should be used, false otherwise | ||
| */ | ||
| function useProviderRegistries () { | ||
| return _hasProviderSupport() && isFileBasedScript(); | ||
| } | ||
|
|
||
| module.exports = { | ||
| isFileBasedScript, | ||
| useProviderRegistries | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| 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 | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.