Skip to content

Commit

Permalink
Items: Support config of channels & metadata in JSON style
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
  • Loading branch information
florian-h05 committed Apr 16, 2022
1 parent e8fca79 commit e39ef52
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 39 deletions.
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,27 +299,40 @@ Calling `addItem(itemConfig)` or `replaceItem(itemConfig)` requires the `itemCon
* .category (icon) ⇒ <code>String</code>
* .groups ⇒ <code>Array.&lt;String&gt;</code>
* .tags ⇒ <code>Array.&lt;String&gt;</code>
* .channellink ⇒ <code>String|Array.&lt;String&gt;</code>
* .metadata ⇒ <code>Map</code>
* .channels ⇒ <code>String|Object { channeluid: { config } }</code>
* .metadata ⇒ <code>Object { namespace: value }|Object { namespace: { value: value , config: { config } } }</code>
* .giBaseType ⇒ <code>String</code>
* .groupFunction ⇒ <code>String</code>

Note: `.type` and `.name` are required.

Example:
```javascript
var metadata = new Map();
metadata.set('expire', '10m,state=OFF');

items.replaceItem({
type: 'Switch',
name: 'Hallway_Light',
label: 'Hallway Light',
category: 'light',
groups: ['Hallway', 'Light'],
tags: ['Lightbulb'],
channellink: 'binding:thing:device:hallway:light',
metadata: metadata
channels: {
'binding:thing:device:hallway#light': {},
'binding:thing:device:livingroom#light': {
profile: 'system:follow'
}
},
metadata: {
expire: '10m,command=OFF',
stateDescription: {
config: {
pattern: '%d%%',
min: 0,
max: 100,
step: 1,
options: '1=Red, 2=Green, 3=Blue'
}
}
}
});
```

Expand Down
43 changes: 31 additions & 12 deletions items/managed.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ const createItem = function (itemConfig) {

let baseItem;
if (itemConfig.type === 'Group' && typeof itemConfig.giBaseType !== 'undefined') {
baseItem = itemBuilderFactory.newItemBuilder(itemConfig.giBaseType, itemConfig.name + "_baseItem").build()
baseItem = itemBuilderFactory.newItemBuilder(itemConfig.giBaseType, itemConfig.name + '_baseItem').build();
}
if (itemConfig.type !== 'Group') {
itemConfig.groupFunction = undefined;
Expand Down Expand Up @@ -326,8 +326,8 @@ const createItem = function (itemConfig) {
* @param {String} [itemConfig.category] the category (icon) for the Item
* @param {String[]} [itemConfig.groups] an array of groups the Item is a member of
* @param {String[]} [itemConfig.tags] an array of tags for the Item
* @param {String|String[]} [itemConfig.channellink] a String of one channel link or an array of channel links for the Item
* @param {Map} [itemConfig.metadata] a map of metadata to set on the item
* @param {String|Object} [itemConfig.channels] for single channel link a string or for multiple an object { channeluid: configuration }; configuration is an object
* @param {Object} [itemConfig.metadata] either object { namespace: value } or { namespace: { value: value, config: {} } }
* @param {HostItem} [itemConfig.giBaseType] the group Item base type for the Item
* @param {HostGroupFunction} [itemConfig.groupFunction] the group function used by the Item
* @returns {Item} an {@link items.Item} object
Expand All @@ -337,15 +337,34 @@ const createItem = function (itemConfig) {
const addItem = function (itemConfig) {
const item = createItem(itemConfig);
managedItemProvider.add(item.rawItem);

if (typeof itemConfig.metadata === 'object') {
for (const [key, value] of itemConfig.metadata) metadata.upsertValue(itemConfig.name, key, value);
}
if (typeof itemConfig.channellink === 'object') {
for (const i in itemConfig.channels) metadata.itemchannellink.addItemChannelLink(itemConfig.name, itemConfig.channels[i]);
const namespace = Object.keys(itemConfig.metadata);
for (const i in namespace) {
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);
} else if (typeof namespaceValue === 'object') { // namespace as key and { value: 'string', configuration: object } as value
const config = new Map();
if (namespaceValue.config != null) {
const configKeys = Object.keys(namespaceValue.config);
for (const i in configKeys) config.set(configKeys[i], namespaceValue.config[configKeys[i]]);
}
metadata.upsertValue(itemConfig.name, namespace[i], namespaceValue.value, config);
}
}
}
if (typeof itemConfig.channellink === 'string') {
metadata.itemchannellink.addItemChannelLink(itemConfig.name, itemConfig.channellink);

if (itemConfig.type !== 'Group') {
if (typeof itemConfig.channels === 'string') { // single channel link with string
metadata.itemchannellink.upsertItemChannelLink(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]]);
}
}

return getItem(itemConfig.name);
};

Expand All @@ -361,7 +380,7 @@ const removeItem = function (itemOrItemName) {

if (typeof itemOrItemName === 'string') {
itemName = itemOrItemName;
} else if (itemOrItemName.hasOwnProperty('name')) {
} else if (itemOrItemName.hasOwnProperty('name')) { // eslint-disable-line no-prototype-builtins
itemName = itemOrItemName.name;
} else {
log.warn('Item not registered (or supplied name is not a string) so cannot be removed');
Expand Down Expand Up @@ -400,8 +419,8 @@ const removeItem = function (itemOrItemName) {
* @param {String} [itemConfig.category] the category (icon) for the Item
* @param {String[]} [itemConfig.groups] an array of groups the Item is a member of
* @param {String[]} [itemConfig.tags] an array of tags for the Item
* @param {String|String[]} [itemConfig.channellink] a String of one channel link or an array of channel links for the Item
* @param {Map} [itemConfig.metadata] a map of metadata to set on the item
* @param {String|Object} [itemConfig.channels] for single channel link a string or for multiple an object { channeluid: configuration }; configuration is an object
* @param {Object} [itemConfig.metadata] either object { namespace: value } or { namespace: { value: value, config: {} } }
* @param {HostItem} [itemConfig.giBaseType] the group Item base type for the Item
* @param {HostGroupFunction} [itemConfig.groupFunction] the group function used by the Item
* @returns {Item} an {@link items.Item} object
Expand Down
84 changes: 71 additions & 13 deletions metadata/itemchannellink.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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');

/**
Expand All @@ -12,44 +13,101 @@ const managedItemChannelLinkProvider = osgi.getService('org.openhab.core.thing.l
* @memberof metadata
* @param {String} itemName the name of the Item
* @param {String} channel channelUID as string
* @param {Map} [conf] Map of channel configuration
* @returns {ItemChannelLink} ItemChannelLink object
*/
const createItemChannelLink = function (itemName, channel) {
log.debug('Creating item channel link {} -> {}', itemName, channel);
return new ItemChannelLink(itemName, new ChannelUID(channel));
const createItemChannelLink = function (itemName, channel, conf) {
log.debug('Creating Item channel link {} -> {}', itemName, channel);
if (typeof conf === 'object') {
log.debug(' .. with configuration.');
return new ItemChannelLink(itemName, new ChannelUID(channel), new Configuration(conf));
} else {
return new ItemChannelLink(itemName, new ChannelUID(channel));
}
};

/**
* Adds a new ItemChannelLink to the registry. That means, it adds a channel link to a given Item.
* Gets an ItemChannelLink from the provider.
*
* @private
* @memberof metadata
* @param {String} itemName the name of the Item
* @param {String} channel channelUID as string
* @returns {ItemChannelLink|null} if ItemChannelLink is registered in provider, ItemChannelLink, else null
*/
const getItemChannelLink = function (itemName, channel) {
log.debug('Getting Item channel link {} -> {} from provider', itemName, channel);
return managedItemChannelLinkProvider.get(itemName + ' -> ' + channel);
};

/**
* Adds a new ItemChannelLink to the provider. Therefore, it adds a channel link to an Item.
*
* @private
* @memberof metadata
* @param {String} itemName the name of the Item
* @param {String} channel channelUID as string
* @param {Map<String, any>} [conf] Map of channel configuration
* @returns {ItemChannelLink} ItemChannelLink object
*/
const addItemChannelLink = function (itemName, channel) {
log.debug('Adding item channel link {} -> {}', itemName, channel);
const itemChannelLink = createItemChannelLink(itemName, channel);
const addItemChannelLink = function (itemName, channel, conf) {
log.debug('Adding Item channel link {} -> {} to provider', itemName, channel);
const itemChannelLink = createItemChannelLink(itemName, channel, conf);
managedItemChannelLinkProvider.add(itemChannelLink);
return itemChannelLink;
};

/**
* Adds a new ItemChannelLink to the registry. That means, it removes a channel link from a given Item.
* Update an ItemChannelLink in the provider. Therefore, it updates the channel link of an Item.
*
* @private
* @memberof metadata
* @param {String} itemName the name of the Item
* @param {String} channel channelUID as string
* @param {Map<String, String>} [conf] Map of channel configuration
* @returns {ItemChannelLink} ItemChannelLink object
*/
const removeItemChannelLink = function (itemName, channel) {
log.debug('Adding item channel link {} -> {}', itemName, channel);
const itemChannelLink = createItemChannelLink(itemName, channel);
managedItemChannelLinkProvider.remove(itemChannelLink);
const updateItemChannelLink = function (itemName, channel, conf) {
log.debug('Updating Item channel link {} -> {} in provider', itemName, channel);
const itemChannelLink = createItemChannelLink(itemName, channel, conf);
managedItemChannelLinkProvider.update(itemChannelLink);
return itemChannelLink;
};

/**
* Adds (inserts) or updates an Item channel link.
*
* @memberof metadata
* @param {String} itemName the name of the Item
* @param {String} channel channelUID as string
* @param {Map<String, String>} [conf] Map of channel configuration
* @returns true if the channel link was added, false if it was updated
*/
const upsertItemChannelLink = function (itemName, channel, conf) {
const existing = getItemChannelLink(itemName, channel);
if (existing === null) {
addItemChannelLink(itemName, channel, conf);
return true;
} else {
updateItemChannelLink(itemName, channel, conf);
return false;
}
};

/**
* Removes an ItemChannelLink from the provider. Therefore, the channel link is removed from the Item.
*
* @memberof metadata
* @param {String} itemName the name of the Item
* @param {String} channel channelUID as string
* @returns {ItemChannelLink} the removed ItemChannelLink
*/
const removeItemChannelLink = function (itemName, channel) {
log.debug('Removing Item channel link {} -> {} from provider', itemName, channel);
return managedItemChannelLinkProvider.remove(itemName + ' -> ' + channel);
};

module.exports = {
addItemChannelLink,
upsertItemChannelLink,
removeItemChannelLink
};
15 changes: 8 additions & 7 deletions metadata/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ const getValue = function (itemName, namespace) {
return result ? result.value : null;
};

const addValue = function (itemName, namespace, value) {
const addValue = function (itemName, namespace, value, config) {
const key = new MetadataKey(namespace, itemName);
MetadataRegistry.add(new Metadata(key, value, {}));
MetadataRegistry.add(new Metadata(key, value, config));
};

const updateValue = function (itemName, namespace, value) {
const metadata = createMetadata(itemName, namespace, value);
const updateValue = function (itemName, namespace, value, config) {
const metadata = createMetadata(itemName, namespace, value, config);
const result = MetadataRegistry.update(metadata);
return result ? result.value : null;
};
Expand All @@ -44,16 +44,17 @@ const updateValue = function (itemName, namespace, value) {
* @param {String} itemName the name of the Item
* @param {String} namespace the name of the namespace
* @param {String} value the value to insert or update
* @param {Map<String, String>} [config] configuration of namespace
* @returns {Boolean} true if the value was added, false if it was updated
*/
const upsertValue = function (itemName, namespace, value) {
const upsertValue = function (itemName, namespace, value, config) {
const existing = getValue(itemName, namespace);

if (existing === null) {
addValue(itemName, namespace, value);
addValue(itemName, namespace, value, config);
return true;
} else {
updateValue(itemName, namespace, value);
updateValue(itemName, namespace, value, config);
return false;
}
};
Expand Down

0 comments on commit e39ef52

Please sign in to comment.