Developing add-on widgets for main UI: possible, how-to or alternatives?

Hi all, I recently started to look into developing custom widgets for Main UI and am now at a point where I am unsure if and how to proceed.

As per the docs, I started playing around with the YAML-based widget editor available in the OpenHab UI. While this got me quite far, it certainly feels at some point cumbersome and limited (see points below). So I looked around the docs and the source code to find out if and how it is possible to implement widgets “natively”, i.e. in the same way the oh-* widgets are implemented (as a Vue.js components). However, from what I could gather (e.g. openhab-webui/bundles/org.openhab.ui/CONTRIBUTING.md at main · openhab/openhab-webui · GitHub), it is only possible to implement those components as part of the OpenHab Main UI bundle, but not as a separate add-on.

Based on my current understanding, I have been thinking about either contributing some improvements and/or features for the YAML-based widgets or to come up with a way how to enable add-ons to provide custom widgets. But either way, it may well be that I am misunderstanding what is possible now, so before I spend more time on that I want to get some input from the community here. In particular the question if and how it is possible to provide Vue.js components as an add-on.

Here are the things I could not find a good solution for with the current YAML-based widgets

  • Access more item information (not only name and state), e.g. the label or possibly even navigating to the parent item (apparently, oh-repeater allows to do that for items in a group or based on a tag, but for single items I couldn’t find a way to “abuse” that).
  • Define functions and constants on the “root” level, similar to oh-context (I tried using oh-context as my root component, but that did break the preview at least for list items).
  • Use/re-use custom widgets in other custom widgets (example: I wanted to have an icon/label widget with some custom behavior in multiple places and widgets, but I can only use the predefined oh-* or f7-* widgets in the slots of a widget).
  • Versioning / source code management (of course I can copy/pasting the YAML from the editor into a local file in a git repository, but that gets old very fast).

I would appreciate any input on either how to solve any of these points or about the more general direction how to proceed.

1 Like

No, you can call a widget in a widget with

slots: 
  default:
    - component: widget:customWidgetName
1 Like

It is my understanding that this was a deliberate design choice.

OH 4 now has an oh-card widget which is the root for all other oh-card widgets. Maybe that can be used here? I’m definitely not a big widget developer so I could be completely off base.

Everything done though the UI is saved in $OH_USERDATA. Widgets are saved in the $OH_USERDATA/jsondb/ui_components*.json files. These are JSON formatted plain text files and order of entries are preserved. They can be saved to git and get reasonable track changes and such.

It’s not as good as every widget in it’s own file but it’s better than copy and paste of the YAML to a file.

This might be possible, I can’t say for sure. Certainly this would be a big enough shift in the philosophy of the UI that you should open an issue in the UI repo to discuss it with the UI maintainers before you put any time into actual development.

There was also a hint that Vue 3 might allow for live addition of vue files to a running instance which would mean you wouldn’t even need to develop a whole add-on just a means for the UI to have access to user vue files. I have not tried to verify this myself, and the UI currently uses Vue2, but there is a PR open to update the UI to Vue3 that has stalled somewhat so you may be able to put some assistance in there to move towards your goal.

That is correct. The basic access to item states is through the special items object and to keep that object from bogging down the UI it has only the limited information available in it.

By contrast, the repeater gets its information from a direct API call and so it contains the complete item objects as returned by that API call. It is possible to get a single item returned by the repeater either through the use of individual tags, groups, or by use of the repeater’s filter property to pare down a larger result. This should be done sparingly, however, because, each repeater is an individual API call, and stacking too many will have a noticeable impact on the responsiveness of a page for most systems.

You should be able to use the context as the root of most widgets. I wouldn’t think that it breaks a list widget, but I haven’t looked into that specifically. It does depend on vue fragments and I don’t know if those interfere with f7 rendering of lists. If you want to explore this further go ahead and start a new thread with your widget code and we can look at it.

Thanks everyone for their quick responses and helpful comments.

In particular the thing with using a custom widget in another custom widget is something I could have found out myself since the “widget:” prefix is also used when referencing a widget in a page (though I actually did not look at the page source code too closely). Maybe that is something that could be more explicitly stated in the docs as I think it can really help in managing complex widgets.

Regarding the access of more item information, I somehow understand the performance impact that may have (but I also have developed a few APIs over the years and I’d say providing a more complete item description should be manageable; but that is also easy to say without having actually spent time with the overall design requirements and quality attributes). On the other hand, as oh-repeater exists (which makes at least one API call) I think it would still be great to have the option to basically do the same just for a single item without having to jump through hoops with e.g. assigning a unique tag or similar. Maybe I spend some time looking into how that could be done in a simple and maybe even reasonably performant way.

As of now, the custom Vue.js thing seems to be too big for me to get involved in, but I’ll keep it in mind.

1 Like

The issue seems to be that there is code in place for the preview to wrap any list item widget in a list container so it can be rendered correctly (see openhab-webui/bundles/org.openhab.ui/web/src/pages/developer/widgets/widget-edit.vue at 3861fa8f272b08d827dd6e25375b87a2c5e6bc93 · openhab/openhab-webui · GitHub). When you use oh-context as the root component with e.g. a f7-list-item as its inner component in the default slot that is not picked up as a “list item widget” and it is rendered as-is.

The widget actually still works normally when used in a page, but developing the widget properly is almost impossible that way.

Here is a minimal example to see what happens:

uid: widget_6cf6d7848d
tags: []
props:
  parameters: []
  parameterGroups: []
component: oh-context
config: {}
slots:
  default:
    - component: oh-list-item
      config:
        title: "This is the title."
        footer: "This is a footer text."
        text: "Some text."

I think it would be much simpler to deal with that if the overall “custom widget” had the oh-context functionality built-in (i.e. I could just define constants or functions in the root level config). That would save one nesting level and the code for detecting if the widget is a list-item would work exactly as written now.

2 Likes