I’m trying to develop a custom list widget. So far so good, the new widget shows up in tabs of the Home Page (ie. Locations/Equipment/Properties Tabs). I would like to have the Item Context Label that shows up in the default widgets in my custom widget. Is this information available for custom developed widgets to reference in a slot?
Unfortunately, not really. The full breadcrumb trail of the semantic tree is not stored in an item it must be recursively determined. This is not too resource intensive when it’s done in bulk by the UI, but it would be completely prohibitive if it need to be done separately for each custom widget.
Thanks Justin. That makes sense.
Let’s think of another approach: I could store the information ‘offline’ in the item metadata. But then…how do I reference that in a list-item widget? Are there any guides/tutorials/topics that can guide me towards metadata access in a default list item widget?
As I understand it you don’t. Item metadata is only available to the oh-repeater widget. All other widgets only have the Item’s state and displayState.
Rich is correct, the only access to metadata in the widget expressions is the oh-repeater. The information that you can access about the items (the items object) is a specially created and curated object that the UI uses to minimize the amount of information passed between the server and the UI instance. If every widget collected the entire information bundle about every item resource utilization would be through the roof and the UI would slow to a crawl on many systems.
The oh-repeater is the one exception to this. When you use the itemsInGroup or itemsWithTags options of the repeater it makes a call to the OH API to get the complete information bundle about each of the items included in those sets (again, not every item unless you use some trick to make one of those sets all items). This can include the item metadata if you want it to.
This means that a system like you are proposing:
would technically work, but is still not a very good idea from a resource perspective.
It would look like this:
- A rule that runs occasionally, extracts all the semantic data pathways from every item (or at least all items of interest), and creates a string of that semantic path which it stores in some item metadata
- Each custom widget uses an
oh-repeater(with either of the item based arrays depending on how you’ve structured your system) and the repeater’s filter property to filter down the resultant array to only the single item that matches the name of the widgets items.
The problem here, is that when you then have the widget appear in one of the semantic tabs, its gong to appear many times and each one of them is going to make an independent API call. If you only have 3 or 4, you probably won’t notice too much of a performance hit, but as the number grows you will definitely notice a slowdown particularly if the connection between the server and the UI instance is slow.
If you are on 4.2.1 or a recent snapshot*, however, there may be a related solution that is fairly easy and bypasses the API calls. Instead of storing the semantic path information in item metadata, you can store it all in its own string item. This becomes much easier to use in a custom widget especially if you have access to the new oh-context component. In that case the setup looks something like this:
- You rule gathers all the required semantic information into a json object with the item names as keys and the semantic path as values. E.g.
{"item_name1": "location > location2 > equip", "item_name2": "location > location3 > equip2"}and stores that json string in a String Item. - You use the
oh-contextcomponent to reconstitute that json string as an constant object in the widget:
- oh-context:
config:
constants:
pathList: =JSON.parse(@@'nameOfStringItem')
slots:
default:
rest of widget...
Then when you want to display the semantic path:
footer: =const.pathList[props.nameOfItemParameter]
The biggest hurdle with that setup is just getting the information into the String Item in the first place. I’ve proposed using a rule to do it so you can just re-run it anytime you change or add to the semantic model, but you could, of course, just maintain the item manually if you really wanted to.
*- Actually, now that I think about it the oh-context is convenient, but not required. You could accomplish the same thing by parsing the storage string item directly in the same expression that you extract the relevent value.
Oh wow! Thanks! This works flawlessly! For reference I’ve included my code:
Here is my javascript code for creating the modelpath, probably can be coded more sophisticated, but I’m no expert and this does the job
. I’ve setup a rule that runs every hour.
"use strict";
(function(data){
// ---------------- Script Logger settings
var sLoggernames = "org.openhab.rule." + ctx.ruleUID;
console.loggerName = sLoggernames;
osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'INFO');
console.info("Populating modelPaths for items with a custom listwidget");
// ---------------- Script Configuration Settings
const targetItem = "str_listwidget_modelpaths"; /// Contains the item name to write the modelPaths to
// ---------------- Functions
function itemPathLabel (item) {
// Retrieve the equipment and the location for the item
let eq=item.semantics.equipment;
let loc=item.semantics.location;
// Setup the modelPath return string
let modelPath = (eq) ? eq.label : "";
// Exit when the item isn't configured in an equipment group
if (!modelPath.length) return '(?) > ' + item.name;
// Traverse the semantic model locations to build the path
while (loc) {
// Prepend the location label to the modelPath
modelPath = loc.label + " > " + modelPath;
// Get the location of this location
loc = items.getItem(loc.name).semantics.location;
}
return modelPath;
}
// ---------------- Globals
var modelPaths = {}; /// Stores the computed modelPaths of the items
// ---------------- Main
// Loop through all items that have a list-widget configured
var subjects=items.getItems().filter( i => { return (i.getMetadata("listWidget")) ? true : false }).forEach( item => {
modelPaths[item.name] = itemPathLabel(item);
});
items.getItem(targetItem).postUpdate(JSON.stringify(modelPaths));
console.debug(JSON.stringify(modelPaths));
})(this.event);
The list item Widget is setup according to your advice, but without the oh-context (couldn’t get it to work for this widget, but I’ll look into it later. The widget isn’t fully functional yet, but the modelPath is shown correctly in the footer!
uid: battery_list_widget
tags:
- battery
- list
props:
parameters:
- description: The label for the widget
label: Title
name: title
required: false
type: TEXT
groupName: main
- context: item
description: The light switch Item
label: Item
name: item
required: false
type: TEXT
groupName: main
- default: "90"
description: The percentage above which green & 4 bars is used
label: Green & 4 bars Level
name: fourbars
required: false
type: INTEGER
min: 0
max: 100
groupName: levels
- default: "70"
description: The percentage above which green & 3 bars are used
label: Green & 3 bars Level
name: threebars
required: false
type: INTEGER
min: 0
max: 100
groupName: levels
- default: "45"
description: The percentage above which orange & 2 bars are used
label: Orange & 2 bars Level
name: twobars
required: false
type: INTEGER
min: 0
max: 100
groupName: levels
- default: "15"
description: The percentage above which orange & 1 bar are used
label: Orange & 1 bar level
name: onebar
required: false
type: INTEGER
min: 0
max: 100
groupName: levels
parameterGroups:
- name: main
label: Main configuration
- name: levels
label: Levels for icon & color selection
timestamp: Aug 28, 2024, 8:45:50 AM
component: oh-list-item
config:
badge: =@props.item
badgeColor: '=(#props.item > props.threebars) ? "darkgreen" : (#props.item >
props.onebar) ? "darkorange" : "darkred"'
icon: '=(#props.item > props.fourbars) ? "if:tabler:battery-4" : (#props.item >
props.threebars) ? "if:tabler:battery-3" : (#props.item > props.twobars) ?
"if:tabler:battery-2" : (#props.item > props.onebar) ? "if:tabler:battery-1"
: "if:tabler:battery"'
iconColor: '=(#props.item > props.threebars) ? "darkgreen" : (#props.item >
props.onebar) ? "darkorange" : "darkred"'
item: =props.item
title: =props.title
footer: =JSON.parse(@@'str_listwidget_modelpaths')[props.item]
This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.
