Using metadata to control room lights in jython scripts

Openhabian on Pi 4 4GB running openHAB 3.2.0 Build #2454

I’m writing jython scripts to control various room activities and I would like to make some generic python functions to control rooms using metadata to influence timing of lights etc … i.e. I want to set custom or built in metadata with various attributes such as light on time duration and brightness levels for difference scenarios …

Question: is there a built-in jython function for reading the metadata or some psuedo code I can follow? I’ve seen various discussion about this for DSL Rules so I know it is being done. Is it working in OH3?

That uses the Helper Libraries.

To access metadata without the Helper Libraries see openhab-helper-libraries/metadata.py at ivans-updates · CrazyIvan359/openhab-helper-libraries · GitHub

In JavaScript without the Helper Libraries I’ve this library:

(function(context) {
  'use strict';

  // Use the logger from the caller if available, otherwise create a Metadata logger
  var log = (context.logger === undefined) ? Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Metadata") : context.logger;

  // Get the metadata registry and relevant classes
  var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
  var _bundle = FrameworkUtil.getBundle(scriptExtension.class);
  var bundle_context = _bundle.getBundleContext()
  var MetadataRegistry_Ref = bundle_context.getServiceReference("org.openhab.core.items.MetadataRegistry");
  var MetadataRegistry = bundle_context.getService(MetadataRegistry_Ref);
  var Metadata = Java.type("org.openhab.core.items.Metadata");
  var MetadataKey = Java.type("org.openhab.core.items.MetadataKey");

  /**
   * Merges two configruation maps.
   * param{object} origConf original configuration
   * param{object} newConf new configuration
   */ 
  context._mergeConf = function(origConf, newConf) {
        var merged = {};
        for (var property in origConf) {
            merged[property] = origConf[property];
        }

        for (var property in newConf) {
            merged[property] = newConf[property];
        }
        return merged;
    }

  /**
   * Deletes the specified metadata on the Item.
   * @param {string} itemName name of the item to delete the metadata from
   * @param {string} namespace namespace to delete, if not specificed delete all metadata
   */
  context.deleteMetadata = function(itemName, namespace){
    namespace = namespace || null;
    if(!namespace){
      log.warn("Deleting ALL metadata on Item " + itemName);
      MetadataRegistry.removeItemMetadata(itemName);
    }
    else {
      MetadataRegistry.remove(new MetadataKey(namespace, itemName));
    }	    
  };

  /**
   * Sets the full metadata on the passed in Item.
   * @param {string} itemName the name of the Item to set the metadata on
   * @param {string} namespace metadata namespace
   * @param {string} value metadata value for the namespace
   * @param {object} configuration metadata configuration
   * @param {bool} overwrite, when true the namespace will be deleted before updated
   */
  context.setMetadata = function(itemName, namespace, value, configuration, overwrite) {
    overwrite = overwrite || false;
    value = value || null;
    if(overwrite){
      deleteMetadata(itemName, namespace);
    }
    var curr = getMetadata(itemName, namespace);

    var key = new MetadataKey(namespace, itemName);

    // If overwriting or there is no metadata, just add it
    if(overwrite || curr === null){
      var metadata = new Metadata(key, value, configuration);
      MetadataRegistry.add(metadata);
    }
    // Merge the changes with what already there
    else {
      if(!value){
        value = curr.value
      }
      var mergedConf = _mergeConf(curr.configuration, configuration);
      MetadataRegistry.update(new Metadata(key, value, mergedConf));
    }	    
  };	  

  /**
   * Sets or updates the Item metadata's value.
   * @param {string} itemName the name of the item
   * @param {string} namespace
   * @param {string} value
   */
  context.setMetadataValue = function(itemName, namespace, value) {
    var curr = getMetadata(itemName, namespace);
    if (curr) {
      setMetadata(itemName, namespace, value, curr.configuration, true);
    } else {
      setMetadata(itemName, namespace, value, {}, false);
    }
  };

  /**
   * Returns the metadata on the passed in item name with the given namespace.
   * @param {string} itemName name of the item to search the metadata on
   * @param {string} namespace namespace of the metadata to return
   * @return {Metadata} the value and configuration or null if the metadata doesn't exist
   */
  context.getMetadata = function(itemName, namespace) {
    return MetadataRegistry.get(new MetadataKey(namespace, itemName));
  };

  /**
   * Returns the value of the indicated namespace
   * @param {string} itemName name of the item to search the etadata on
   * @param {string} namespace namespace to get the value from
   * @return {string} The value of the given namespace on the given Item, or null if it doesn't exist
   */
  context.getMetadataValue = function(itemName, namespace) {
    var md = getMetadata(itemName, namespace);
    return (md === null) ? null : md.value;
  };

  /**
   * Returns the configuration of the given key in the given namespace
   * @param {string} itemName name of the item to search for metadata on
   * @param {string} namespace namespace of the metadata
   * @param {string} key name of the value from config to return
   * @return {string} the value assocaited with the key, null otherwise
   */
  context.getMetadataKeyValue = function(itemName, namespace, key){
    var md = getMetadata(itemName, namespace);
    if(md === null){
      return null;
    }
    return (md.configuration[key] === undefined) ? null : md.configuration[key];
  };

  /**
   * Returns the value of the name metadata, or itemName if name metadata doesn't exist on the item
   * @param {string} itemName name of the Item to pull the human friendly name metdata from
   */
  context.getName = function(itemName) {
    var name = getMetadataValue(itemName, "name");
    return (name === null) ? itemName : name;
  };

  /**
   * Filters the members of the passed in group and generates a comma separated list of
   * the item names (based on metadata if available).
   * @param {string} groupName name of the group to generate the list of names from
   * @param {function} filterFunc filtering function that takes one Item as an argument
   */
  context.getNames = function(groupName, filterFunc) {
    var Collectors = Java.type("java.util.stream.Collectors");
    return context.ir.getItem(groupName)
                     .members
                     .stream()
                     .filter(filterFunc)
                     .map(function(i) {
                       return context.getName(i.name);
                     })
                     .collect(Collectors.joining(", "));
  };

})(this)

Or in a more concise “just the parts I need” example:

// Accessing Item Metadata
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Examples");
logger.info("Trying to extract a metadata value")
var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
var _bundle = FrameworkUtil.getBundle(scriptExtension.class);
var bundle_context = _bundle.getBundleContext()
var classname = "org.openhab.core.items.MetadataRegistry"
var MetadataRegistry_Ref = bundle_context.getServiceReference("org.openhab.core.items.MetadataRegistry");
var MetadataRegistry = bundle_context.getService(MetadataRegistry_Ref);
var methods = MetadataRegistry.class.getDeclaredMethods();
logger.info("There are " + methods.length + " methods");
for each (var m in methods) {
  logger.info(m.toString());
}
var Metadata = Java.type("org.openhab.core.items.Metadata");
var MetadataKey = Java.type("org.openhab.core.items.MetadataKey");
var metadata = MetadataRegistry.get(new MetadataKey("name", "vArgus_Status"));
logger.info("vArgus_Status's name is " + metadata.value);
2 Likes