Edit: I’ve started some more formal tutorials. You can find the first at OH 3 Examples: How to boot strap the state of an Item
Model
I decided to experiment with the model page for a bit. I don’t have much to model yet, just a bunch of Network Binding Things/Items but I was able to get started at least. I fully modeled my locations (I’ve two locations represented in openHAB) with an indoor and outdoor. Indoor is split by floor and each floor is divided by rooms. Then I put all my Items into the appropriate room.
It’s really nice to be able to add the Groups for Locations and Equipment from the Model. But I wish I could add already existing Items and Groups to the model. To do that you have to go to the Items menu, find the Item and edit it to add the Semantic tags. I’ve already filed an issue on this.
Building the Items is nice as it automatically populates the pages. But this shows how it’s more important to edit the defaults for the Items so they all get represented consistently and attractively. The Location tab though did not get populated. Not sure about that. I think I read that tab requires more configuration.
Equipment
Properties
The ways that these appear on the pages is defined by the default card and list on the Item.
So let’s change how the Item appears in the list widgets in the two pages above. Open the “Default List Item Widget”. I want to use an icon that changes color with the state, use a badge, and have ON read as “ONLINE” and off read as “OFFLINE”. So I open the entry and choose a “Label List Item” from the list of options.
I was able to get pretty close to what I wanted but I had a hard time figuring out how to suppress the Item’s state.
I clicked on “Show advanced” and defined the Title, Icon as oh:network
and turned on “Icon depends on state”. That handles the wifi signal icon coloring on the left.
Then I opened the code and added a badge and badgeColor.
value: oh-label-item
config:
badgeColor: '=(items.vMBR_Shelly_Status.state === "ON") ? "green" : "red"'
icon: oh:network
badge: '=(items.vMBR_Shelly_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
iconUseState: true
title: Master Bedroom Shelly 1 Status
I then experimented with all sorts of options for the “Pattern” under stateDescription
trying to suppress the Item’s state to no avail. What finally ended up working was to set the “Pattern” to " " which I did through code.
Equipment
Properties
NOTE: I learned about how to set a badge with help from Yannick and Rainer on the documentation thread.
So, if one builds a reasonable model and configures the default widgets for each Item in the model, a custom UI may not even need to be created! Very cool!
Custom Panels
So I’ve got a ton of status Items I’d like to have a widget that collapses the statuses so at the top level I see the over all status (ON if all ON else OFF type Group). Below that I see all the home automation related devices, also aggregating the status of the services they run. Then under those I see the status of all the services. The statuses will be populated by the Network binding and MQTT LWTs.
This took a bit of trial and error and a whole lot of help from Yannick and Rainer but I managed to get it to work.
I created an oh-list-card and then opened the code so I could edit the widget manually.
This sort of collapsing list is called an accordion list. So I had to create the hierarchy of oh-list-card, oh-list, and oh-list-item entries. The hierarchy is like this:
oh-list-card
oh-list-item
oh-list
of-list-item
oh-list
oh-list-item
This will get you to the third level.
The widget looks like:
The full YAML (which is way overkill but here it is anyway):
component: oh-list-card
config:
accordionList: true
title: All Services Status
footer: Online status of home automation related services
slots:
default:
- component: oh-list-item
config:
title: All Services
subtitle: When OFF one or more services are offline
badge: '=(items.ServiceStatuses.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.ServiceStatuses.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: argus
subtitle: Home automation server
badge: '=(items.vArgus_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vArgus_Status.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: Grafana
badge: '=(items.vGrafana_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vGrafana_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Grafana Image Renderer
badge: '=(items.vGrafana_IR_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vGrafana_IR_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: InfluxDB
badge: '=(items.vInfluxdb_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vInfluxdb_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Mosquitto
badge: '=(items.vMosquitto_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vMosquitto_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Portainer Agent
badge: '=(items.vArgus_Portainer_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vArgus_Portainer_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: cerberos
subtitle: Garage sensors and controller
badge: '=(items.vCerberos_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vCerberos_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: fafnir
subtitle: NAS Server
badge: '=(items.vFafnir_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vFafnir_Status.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: OMV Service
badge: '=(items.vOMV_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vOMV_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: OMV NFS
badge: '=(items.vOMV_NFS_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vOMV_NFS_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: OMV Timemachine
badge: '=(items.vOMV_AFP_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vOMV_AFP_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: huginn
subtitle: Virtual Desktop
badge: '=(items.vHuginn_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vHuginn_Status.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: Portainer Server
badge: '=(items.vHuginn_Portainer_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vHuginn_Portainer_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: hydra
subtitle: Doors and windows sensors
badge: '=(items.vHydra_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vHydra_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: manticore
subtitle: NUT and BT bridge
badge: '=(items.vManticore_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vManticore_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: medusa
subtitle: Media services
badge: '=(items.vMedusa_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vMedusa_Status.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: Calibre
badge: '=(items.vCalibre_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vCalibre_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Elasticsearch
badge: '=(items.vElasticsearch_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vElasticsearch_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: GitLab
badge: '=(items.vGit_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vGit_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Guacamole
badge: '=(items.vGuacamole_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vGuacamole_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: NextCloud
badge: '=(items.vNextcloud_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vNextcloud_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Plex Media Server
badge: '=(items.vPlex_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vPlex_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Portainer
badge: '=(items.vMedusa_Portainer_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vMedusa_Portainer_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: PostgreSQL
badge: '=(items.vPostgreSQL_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vPostgreSQL_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Redis
badge: '=(items.vRedis_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vRedis_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: norns
subtitle: Dad's openHAB server
badge: '=(items.vNorns_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vNorns_Status.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: Dad's openHAB
badge: '=(items.vDads_openHAB_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vDads_openHAB_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Devices
subtitle: Individual Devices
badge: '=(items.DeviceStatuses.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.DeviceStatuses.state === "ON") ? "green" : "red"'
slots:
accordion:
- component: oh-list
config:
accordionList: true
class:
- margin-left
slots:
default:
- component: oh-list-item
config:
title: GalaxyNote TinyCam Service
badge: '=(items.vGalaxyNote_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vGalaxyNote_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: HestiaPi
badge: '=(items.vHestiaPi_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vHestiaPi_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: MBR Sensors
badge: '=(items.vMBR_Sensors_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vMBR_Sensors_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: MBR Shelly 1
badge: '=(items.vMBR_Shelly_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vMBR_Shelly_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Nate's NR Sensors
badge: '=(items.vNateBR_Sensors_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vNateBR_Sensors_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Orbi AP
badge: '=(items.vOrbi_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vOrbi_Status.state === "ON") ? "green" : "red"'
- component: oh-list-item
config:
title: Orbi Satellite AP
badge: '=(items.vOrbi_Satellite_Status.state === "ON") ? "ONLINE" : "OFFLINE"'
badgeColor: '=(items.vOrbi_Satellite_Status.state === "ON") ? "green" : "red"'
Here is where I discovered my first mistake. I put a whole bunch of time into that widget and want to preserve it even if I decided to move it around on the Page or put it on other pages. I need this to be a custom widget is it’s stored separately from the Page. Enter the Widgets library.
First, copy all the YAML from the widget you just created to the clipboard. Next open the Developer Tools and then Widgets. Click the + icon and paste the YAML previously copied over the component
and config
entries. Enter meaningful values for the name, label, and such and some reasonable tags. Save it and you’ve now created your own reusable widget.
When adding a new widget to a page, there will be a “Personal Widgets” section you can select it from to drop it onto the page.