got it all running now - thanks for the support!
Great! Glad I could help
I managed to fully configure the widget, but Iâm changing some stuff because I prefer to work without the additional parameters. Everything is working fine, but the displayState
in my items are not showing (yes they are thereâŠ)
footer: =loop.item.displayState
when I use loop.item.state
I get the state value.
If Iâm just getting a displayState of one specific item itâs also working:
footer: =items.AfvalGft.displayState
Full yaml
uid: garbage_list_v2
tags:
- card
props:
parameters: []
parameterGroups: []
timestamp: Mar 4, 2021, 10:17:26 AM
component: f7-card
config:
title: "Trash"
slots:
default:
- component: f7-card-content
slots:
default:
- component: f7-list
config:
mediaList: true
slots:
default:
- component: oh-repeater
config:
for: item
sourceType: itemsInGroup
groupItem: Trash
fragment: true
slots:
default:
- component: oh-list-item
config:
title: =loop.item.label.replace('Afval','')
icon: f7:trash
iconColor: gray
badge: '=((dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 0 ? "Today" : (dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 1 ? "Tomorrow" : false)'
badgeColor: '=((dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 0 ? "red" : "yellow")'
footer: =loop.item.displayState
visible: '=loop.item.state == "UNDEF" ? false : true'
- component: f7-card-footer
slots:
default:
- component: Label
config:
text: '="Next: " + items.Trash.displayState'
Good that you got it do what you wanted! Regarding the displayState: you probably have not added the State Description Metadata to the respective item.
Yes I did, as you can see in the second screenshot (itâs displaying the metadata of one item in all other items)
Do you know by change, what other info we can grab from an item? What are the commands to grab other metadata info (like label, state, displayStateâŠ), I didnât find any docs.
Ah ok. Sorry I have no idea why this does not work for you thenâŠ
Iâm afraid I also cannot answer your second question, I also only know these three properties.
I completed my version of the widget.
All dates are in their corresponding items and these items are member of a general âtrashâ group.
There are no parameters, so everything is gathered from the items. I added the colors of the trashbins as a tag to the items.
If there is no date in the item, it will be hidden. The same will happen when the date is more then 31 days in the future (because âsnoeiafvalâ is only collected 2 times a year, so I donât want to see this the whole year)
uid: trash
tags:
- card
- trash
props:
parameters: []
parameterGroups: []
timestamp: Mar 4, 2021, 5:01:40 PM
component: f7-card
config:
title: Trash Calendar
slots:
default:
- component: oh-list
slots:
default:
- component: oh-repeater
config:
for: item
sourceType: itemsInGroup
groupItem: Trash
fragment: true
slots:
default:
- component: oh-list-item
config:
icon: f7:trash
iconColor: =loop.item.tags[0]
color: gray
title: =loop.item.label.replace('Afval','')
badge: '=((dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 0 ? "Today" : (dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 1 ? "Tomorrow" : (dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) > 1 ? "In " + (dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) + " days" : false)'
badgeColor: '=((dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 0 ? "red" : (dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) == 1 ? "yellow" : "green")'
#footer: =loop.item.state
visible: '=loop.item.state == "UNDEF" ? false : loop.item.state == "NULL" ? false : (dayjs(loop.item.state).diff(dayjs().startOf("day"), "days")) > 31 ? false : true'
- component: f7-card-footer
slots:
default:
- component: Label
config:
text: '="Next: " + items.Trash.displayState'
Hi wars and thank you for sharing your version of the widget. I thought about using metadata for the colors even though custom metadata is not really convenient to maintain in Main UI at the moment. Have you thought about this as well and do you happen to have a code example for that? I donât want to use tags since this is somewhat against the concept of tags and may be error prone if I may end up using the tag feature more in the future.
You mean a custom namespace? I donât know how this works (yet)
You are correct about this, itâs not realy a clean appeoach, because when I will add a tags in the future, the index will change, but I will deal with it when this happens
Another possibility is to add the color into the label and extract this in the widgetcodeâŠ
I played around a bit with custom namespaces and I think that it is not too complicated to add these. However it is not very convenient to maintain these since you would have to remember the namespace names since they are not shown in UI yet. Moreover I havenât found a way to delete a custom namespace yetâŠ
In addition to that I have no idea how to access these namespaces in rules, I found a couple of code examples but these seemed a bit too complicated for my purpose so I did not follow this road until now. I might try this in the future, custom namespaces seem to be just too powerful to ignore this feature. It could be really useful to reduce the number of items I guess.
I am currently trying to add this to my setup, but I do not get how the state description works. Could you elaborate on that one or link me somewhere?
Hi Johannes,
You can add the state description as metadata when you open the item in the UI. See my example config in the screenshot:
The actual configuration depends on your preference. You can read more about state descriptions in the OH documentation:
Thank you!
uid: MĂŒllabfuhr_cell
tags: []
props:
parameters:
- description: "Text Prefix<b> ( Default: Next colltection )</b>"
label: Text
name: prefix
required: false
type: TEXT
- context: item
description: An item with the String of the next Garbage Collection Date
label: Item Date
name: date
required: true
type: TEXT
- context: item
description: An item with the String of the next Garbage Collection Type
label: Item Type
name: type
required: true
type: TEXT
parameterGroups: []
timestamp: May 2, 2021, 12:34:49 PM
component: f7-card
config:
style:
border-radius: var(--f7-card-expandable-border-radius)
min-width: 200px
slots:
default:
- component: f7-block-header
config:
style:
display: flex
flex-direction: row
justify-content: center
padding: 0px
padding-left: 0px
margin: 0px
slots:
default:
- component: Label
config:
style:
padding: 10px
text-align: center
text: '=(props.prefix) ? props.prefix + " " + dayjs(items[props.date].state).fromNow() : "Next collection " + " " + dayjs(items[props.date].state).fromNow()'
- component: f7-block
config:
style:
display: flex
flex-direction: row
justify-content: center
padding: 10px
padding-left: 0px
margin: 0px
height: 70px
slots:
default:
- component: oh-image
config:
visible: "=items[props.type].state.split('/')[0] === 'RestmĂŒll' ? true : false"
url: /icon/tonne_grau.svg
style:
padding-right: 5px
width: 30px
- component: oh-image
config:
visible: "=items[props.type].state.split('/')[0] === 'Gelb' ? true : items[props.type].state.split('/')[1] === 'Gelb' ? true : items[props.type].state.split('/')[2] === 'Gelb' ? true : items[props.type].state.split('/')[3] === 'Gelb' ? true : false"
url: /icon/tonne_gelb.svg
style:
padding-right: 5px
width: 30px
- component: oh-image
config:
visible: "=items[props.type].state.split('/')[0] === 'Bio' ? true : items[props.type].state.split('/')[1] === 'Bio' ? true : items[props.type].state.split('/')[2] === 'Bio' ? true : items[props.type].state.split('/')[3] === 'Bio' ? true : false"
url: /icon/tonne_braun.svg
style:
padding-right: 5px
width: 30px
- component: oh-image
config:
visible: "=items[props.type].state.split('/')[0] === 'Papier' ? true : items[props.type].state.split('/')[1] === 'Papier' ? true : items[props.type].state.split('/')[2] === 'Papier' ? true : items[props.type].state.split('/')[3] === 'Papier' ? true : false"
url: /icon/tonne_blau.svg
style:
width: 30px
- component: oh-image
config:
visible: "=items[props.type].state.split('/')[0] === 'SperrmĂŒll' ? true : items[props.type].state.split('/')[1] === 'SperrmĂŒll' ? true : items[props.type].state.split('/')[2] === 'SperrmĂŒll' ? true : items[props.type].state.split('/')[3] === 'SperrmĂŒll' ? true : false"
url: /icon/sperrmuell.svg
style:
width: 30px
Very nice widget. I already used your widgets for the washing mashine and the universal widget because I like your style very much.
I would also like to use this one but I am not sure how you configured your items.
I already have icalendar in use for my garbage calendar but currently I am just checking the days today and tomorrow. As far as I can see you can see a much longer date range in your widget so I would be gad if you could show me your config for icalendar.
My icalendar things file looks like this:
Bridge icalendar:calendar:private "calendar" [ url="https://calendar.google.com/calendar/ical/xxxxx", refreshTime=5 ]
Thing icalendar:eventfilter:today "Today events" (icalendar:calendar:private) [ maxEvents=4, datetimeUnit="DAY", datetimeStart=0, datetimeEnd=1, datetimeRound=true, refreshTime=5 ]
Thing icalendar:eventfilter:tomorrow "Tomorrows events" (icalendar:calendar:private) [ maxEvents=4, datetimeUnit="DAY", datetimeStart=1, datetimeEnd=2, datetimeRound=true, refreshTime=5 ]
Thing icalendar:eventfilter:day2 "Day2 events" (icalendar:calendar:private) [ maxEvents=4, datetimeUnit="DAY", datetimeStart=2, datetimeEnd=3, datetimeRound=true, refreshTime=5 ]
Hi Eric,
You should not to set the endTime property or use a much higher value. The binding will stop searching at this point in the future and will not return a hit if the event is later than the specified number of days.
I configured my things through the UI. You can see one complete example in the screenshots:
now it is working perfectly! Thank you for this beautyful widget
Dear Ward and all,
Thanks a lot for this excellent widget. It works nearly perfect for me. I really learned a lot when reverse-engineering how you constructed the widget.
Interestingly I have the same issue with the displayState function: for me it delivers no footer at all when integrated in a list. The State Description Metadata is correctly encoded (if I use the garbage_cell_v4 widget it works fine and displays Thursday, 22.05, like I want it to be). This looks like a bug to me. As a workaround, I have opted for the following:
footer: =loop.item.state.substring(0,10)
It doesnât really look like I want it to look like, but it does the job for now and looks less overcrowded than just using the state function.
Does anyone know how to order the list by date, so that the next garbage collection always stays on top of the list?
You could order the list by creating a text item that contains the datearray parameter. You then need a rule that updates the item whenever one of the dates changes. It not super convenient but not too complicated. I donât know any other way to do this.
EDIT: oh and please donât forget to share your code here since this has been requested by other users as well and I would also be interested but was to lazy to implement this myself so far
Letâs do a first try for the sorted list. The script uses one group item containing all garbage filter items (gMullFilter) and one item for the result (SortedGarbageDateItems). Color and naming of the list entries is taken from custom metadata namespace (color and name) of each filter item.
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 logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.rule." + ctx.ruleUID);
var members = Java.from(ir.getItem("gMullFilter").getAllMembers());
members.sort(function(a,b) {
return new Date(a.state) - new Date(b.state);
});
var sortedString = "";
members.forEach(function(item) {
var color = MetadataRegistry.get(new MetadataKey("color", item.name));
var name = MetadataRegistry.get(new MetadataKey("name", item.name));
if ((null != color) || (null != name)) {
sortedString += '"' + name.value + '","' + color.value + '","f7:trash","' + item.name + '"|'
}
})
if (1 < sortedString.length) {
events.postUpdate("SortedGarbageDateItems", sortedString.slice(0, -1));
}