items/metadata/metadata.js

/**
 * @typedef {import('../items').Item} Item
 * @private
 */

/**
 * Item metadata namespace.
 * This namespace provides access to Item metadata.
 *
 * @namespace items.metadata
 */

const osgi = require('../../osgi');
const utils = require('../../utils');
const { _getItemName } = require('../../helpers');

const metadataRegistry = osgi.getService('org.openhab.core.items.MetadataRegistry');
const Metadata = Java.type('org.openhab.core.items.Metadata');
const MetadataKey = Java.type('org.openhab.core.items.MetadataKey');

/**
 * Class representing an openHAB Item metadata namespace
 *
 * @memberof items.metadata
 * @hideconstructor
 */
class ItemMetadata {
  /**
   * @param {*} rawMetadata {@link https://www.openhab.org/javadoc/latest/org/openhab/core/items/metadata org.openhab.core.items.Metadata}
   */
  constructor (rawMetadata) {
    /**
     * metadata namespace's value
     * @type {string}
     */
    this.value = rawMetadata.getValue();
    /**
     * metadata namespace's configuration
     * @type {object}
     */
    this.configuration = utils.javaMapToJsObj(rawMetadata.getConfiguration());
  }
}

/**
 * Gets all metadata from a given Item.
 *
 * @example
 * var meta = items.metadata.getItemMetadata('Kitchen_Light');
 * var namespaces = Array.from(meta.keys());
 * var stateDescription = meta.get('stateDescription');
 *
 * @private
 * @param {string} itemName the name of the Item
 * @returns {{ namespace: ItemMetadata }} object of all Item metadata with one property for each namespace; each namespace is of type {@link ItemMetadata}
 */
function _getAllItemMetadata (itemName) {
  const metadata = {};
  // TODO: Move implementation to openHAB Core
  metadataRegistry.stream().filter((meta) => meta.getUID().getItemName().equals(itemName)).forEach((meta) => {
    metadata[meta.getUID().getNamespace()] = new ItemMetadata(meta);
  });
  return metadata;
}

/**
 * Gets metadata of a single namespace from the given Item.
 *
 * @private
 * @param {string} itemName the name of the Item
 * @param {string} namespace namespace of the metadata
 * @returns {ItemMetadata|null} metadata or `null` if the Item has no metadata under the given namespace
 */
function _getSingleItemMetadata (itemName, namespace) {
  const key = new MetadataKey(namespace, itemName);
  const meta = metadataRegistry.get(key);
  if (meta === null || meta === undefined) return null;
  return new ItemMetadata(meta);
}

/**
 * Gets metadata of a single namespace or of all namespaces from a given Item.
 *
 * @example
 * // Get metadata of ALL namespaces
 * var meta = items.metadata.getMetadata(items.Hallway_Light);
 * var namespaces = Object.keys(meta); // Get metadata namespaces
 * // Get metadata of a single namespace
 * meta = items.metadata.getMetadata(items.Hallway_Light, 'expire');
 *
 * @see items.Item.getMetadata
 * @memberOf items.metadata
 * @param {Item|string} itemOrName {@link Item} or the name of the Item
 * @param {string} [namespace] name of the metadata: if provided, only metadata of this namespace is returned, else all metadata is returned
 * @returns {{ namespace: ItemMetadata }|ItemMetadata|null} all metadata as an object with the namespaces as properties OR metadata of a single namespace or `null` if that namespace doesn't exist; the metadata itself is of type {@link ItemMetadata}
 */
function getMetadata (itemOrName, namespace) {
  const itemName = _getItemName(itemOrName);
  if (namespace !== undefined) return _getSingleItemMetadata(itemName, namespace);
  return _getAllItemMetadata(itemName);
}

/**
 * Updates or adds metadata of a single namespace to an Item.
 *
 * @see items.Item.replaceMetadata
 * @memberof items.metadata
 * @param {Item|string} itemOrName {@link Item} or the name of the Item * @param {string} namespace name of the metadata
 * @param {string} namespace name of the metadata
 * @param {string} value value for this metadata
 * @param {object} [configuration] optional metadata configuration
 * @returns {ItemMetadata|null} old metadata or `null` if the Item has no metadata with the given name
 */
function replaceMetadata (itemOrName, namespace, value, configuration) {
  const key = new MetadataKey(namespace, _getItemName(itemOrName));
  const newMetadata = new Metadata(key, value, configuration);
  const meta = (metadataRegistry.get(key) === null) ? metadataRegistry.add(newMetadata) : metadataRegistry.update(newMetadata);
  if (meta === null || meta === undefined) return null;
  return new ItemMetadata(meta);
}

/**
 * Removes metadata of a single namespace or of all namespaces from a given Item.
 *
 * @see items.Item.removeMetadata
 * @memberof items.metadata
 * @param {Item|string} itemOrName {@link Item} or the name of the Item
 * @param {string} [namespace] name of the metadata: if provided, only metadata of this namespace is removed, else all metadata is removed
 * @returns {ItemMetadata|null} removed metadata OR `null` if the Item has no metadata under the given namespace or all metadata was removed
 */
function removeMetadata (itemOrName, namespace) {
  const itemName = _getItemName(itemOrName);

  if (namespace !== undefined) {
    const key = new MetadataKey(namespace, itemName);
    const meta = metadataRegistry.remove(key);
    if (meta === null || meta === undefined) return null;
    return new ItemMetadata(meta);
  } else {
    return metadataRegistry.removeItemMetadata(itemName);
  }
}

module.exports = {
  getMetadata,
  replaceMetadata,
  removeMetadata,
  itemchannellink: require('./itemchannellink'),
  ItemMetadata
};