Expire in particular is configured using Item Metadata. Metadata on Items is managed completely separately from Items (just like Links to Channels). And it’s much more complex than just a name and value. So adding a function to the Item class is not feasible.
However, except in Rules DSL and Blockly (for now) everything you see in the UI is available to you in rules.
If you are using Jython and the Helper Library see core.metadata — openHAB Helper Libraries documentation. Expire is using the “expire” namespace. You can pull and set metadata no problem.
If using JavaScript I don’t think the Helper Library has metadata support yet. Accessing metadata would look something like the following.
Get access to the metadata registry
// 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");
Get a given metadata namespace
var metadata = MetadataRegistry.get(new MetadataKey("namespace", "itemName"));
Remove a given metadata namespace
MetadataRegistry.remove(new MetadataKey("namespace", "itemName"))
Remove all metadata from an Item
MetadataRegistry.removeItemMetadata("itemName")
Adding metadata is a little complex because it isn’t just a String. It’s a whole data structure consisting of a:
- namespace
- value
- configuration
The namespace is just a string. For Expire it’s “expire”.
The value is also just a string. For expire, that’s the stuff after the “=” sign.
The configuration is denoted by a “[ ]” and is made up of name value pairs. Expire doesn’t use a configuration.
So to change the Expire metadata you first want to delete the old:
MetadataRegistry.remove(new MetadataKey(namespace, itemName));
and then add the new.
var key = new MetadataKey("namespace", "itemName");
var metadata = new Metadata(key, value, {});
MetadataRegistry.add(metadata);
I’ve my own personal Item metadata 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)
But frankly, by the time you’ve gone through all that, why not just create a timer in the first place?
And even if the Item did have a method built into it to mess with the Expire, you’d have to do that from a rule too. The whole point of Expire is to not have to use a rule in the first place for some simple use cases.