Garbage Collection

got it all running now - thanks for the support!

Great! Glad I could help :grinning:

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.
image

If I’m just getting a displayState of one specific item it’s also working:

footer: =items.AfvalGft.displayState

image

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)

image

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'
3 Likes

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)
image

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 :slight_smile:

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? :thinking:

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:

1 Like

Thank you! :slight_smile:

garbagaecollection

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

1 Like

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:



2 Likes

now it is working perfectly! Thank you for this beautyful widget
image

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 :wink:

1 Like

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));
}
1 Like