Change / Edit / Update metadata from (javascript) rules

Hi,

I was trying to make a small helper function to update a item’s metadata from a rule.
But it seems the metadata dictionary is inmutable(?).

This is the function I made (largely copied):

  context.updateMetadataKeyValue = function(itemName, namespace, key, value){
    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 MetadataKey = Java.type("org.openhab.core.items.MetadataKey");   
    var md = MetadataRegistry.get(new MetadataKey(namespace, itemName));
    if(md === null){
      return null;
    }
    var keyitem = (md.configuration[key] === undefined) ? null : md.configuration[key];
    if (keyitem === null){
      return null;
    }
    md.configuration[key] = value; // <--- This is where it all goes wrong :(
    return MetadataRegistry.update(md);
  }

The idea was to be able update a items’s meta data like

updateMetadataKeyValue(event.itemName, "Scene", "sceneTriggerCounter", sceneTriggerCounter);

can it be done? :slight_smile:

I just tested essentially what you presented and it’s working fine for me.

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");

var metadata = MetadataRegistry.get(new MetadataKey("mTest", "TestSwitch"));
metadata.configuration['name'] = 'written metadata';
// metadata.configuration.put('name', 'written metadata'); // this also works
logger.info(metadata.toString());

And viewing the metadata in the item settings shows the updated value as well. What I’m wondering, is the metadata you’re trying to modify defined in .items file(s)? You can not modify metadata that’s defined in .items files from the UI or in code.

Interestingly you can define metadata for items defined in .items files through the UI, however I just tested and you can not modify this using this method either. What you can do though is remove the metadata:

MetadataRegistry.remove(new MetadataKey("mTest", "TestSwitch")); //only works if the metadata was defined through UI

Which would then allow you to construct new metadata

MetadataRegistry.add(new Metadata(new MetadataKey("mTest2", "staticTestSwitch"), "value", {test:"test config item"}))
2 Likes

The metadata was created using openhab UI. I Forgot to mention that I’m on Openhab version 3.1.0

I’ve added custom metadata “Scene”

value: value
config:
  sceneItem: Scene_Diningroom
  sceneTriggerCounter: 0
  sceneToSet: Dinner

When I run your code

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Run From");
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");
var metadata = MetadataRegistry.get(new MetadataKey("Scene", "ZWaveDiningDoubleRelaySwitch2x15kW_Switch1"));
metadata.configuration['sceneToSet'] = 'written metadata';
logger.info(metadata.toString());

which is essentially the same I’ll get :

java.lang.UnsupportedOperationException: null
        at java.util.Collections$UnmodifiableMap.put(Collections.java:1457) ~[?:?]
        at jdk.nashorn.internal.scripts.Script$Recompilation$19824$\^eval\_$cu1$restOf.:program(<eval>:11) ~[?:?]
        at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:655) ~[jdk.scripting.nashorn:?]
        at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:513) ~[jdk.scripting.nashorn:?]
        at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:527) ~[jdk.scripting.nashorn:?]
        at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:456) ~[jdk.scripting.nashorn:?]
        at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:413) ~[jdk.scripting.nashorn:?]
        at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:409) ~[jdk.scripting.nashorn:?]
        at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:162) ~[jdk.scripting.nashorn:?]
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) ~[java.scripting:?]
        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:62) ~[?:?]
        at java.util.Optional.ifPresent(Optional.java:183) ~[?:?]
        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:59) ~[?:?]
        etc.

but, after some fiddling around I found out that this works:

MetadataRegistry.update(new Metadata(new MetadataKey("Scene", "ZWaveDiningDoubleRelaySwitch2x15kW_Switch1"), "value", {sceneToSet:"written metadata"}))

But I don’t know how or why…

So I ended up making a clone of the configuration property and updating that to the meta data registry.

The code is pretty bad to look at, but it seems to work :slight_smile:

This is the end result:

context.updateMetadataKeyValue = function(itemName, namespace, key, value){
    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");   
    var md = MetadataRegistry.get(new MetadataKey(namespace, itemName));
    if(md === null){
      return null;
    }
    var keyitem = (md.configuration[key] === undefined) ? null : md.configuration[key];
    if (keyitem === null){
      return null;
    }    
    var clonemdConfig = copy(md.configuration);
    clonemdConfig[key] = value;    
    MetadataRegistry.update(new Metadata(new MetadataKey(namespace, itemName), "value",  clonemdConfig))
  }

  function copy(aObject) {
    if (!aObject) {
      return aObject;
    }  
    var v;
    var bObject = Array.isArray(aObject) ? [] : {};
    for (var k in aObject) {
      v = aObject[k];
      bObject[k] = (typeof v === "object") ? copy(v) : v;
    }  
    return bObject;
  }