Garbage Collection Widget

Hi everyone,

I’d like to share my first go on a rather simple list widget I created today. Unfortunately everything is hardcoded in this widget so it is not really easy to reuse. However I tried to improve the widget through the use of oh-repeater and props but when it comes down to the colors that should be used for every trash bin it gets quite ugly if you have to pass in a lot of parameters…

Why am I sharing it anyway?

  1. There is still a limited amount of examples around and it took me a while to figure out everything that went into the final version of this widget. So I think it may be nice for someone in my position to have everything i’ve learned today (at least regarding OH3) in one example.
  2. I’m 100% sure that there is a lot of room for improvement so I’m more than happy to receive any constructive comments on what I could have done better (e.g. I want to add some visibility options for the items since especially for the christmas tree collection there a no further dates in my calendar) :slight_smile:
  3. It is always to better to give something in return when you have a question to the community :wink: So here is my question: Any hint on the more efficient use of my date comparisons would be highly appreciated. Right now I compare the date of the collection with a date that is formatted the same way. It would however be much more elegant if this widget would not be relying on the same formatting of two items… I’d love to see some better solutions for that!

BTW: I read all the collection date values from my nextcloud calendar with the iCalendar binding.

This is what the widget looks like:
Bildschirmfoto von 2021-01-20 21-47-20
Note that I changed the code a bit to fake the badges for demonstration purposes. They are normally only shown the day before a garbage collection (in yellow) and on the day of garbage collection (in red).

The text in the widget is in German but I think you get the idea. The badges ‘Heute’ and ‘Morgen’ mean ‘Today’ and ‘Tomorrow’.

uid: garbage_cell_v3
tags: []
props:
  parameterGroups: []
timestamp: Jan 20, 2021, 3:49:25 PM
component: f7-card
config:
  title: ="Abfuhrtermine:"
slots:
  default:
    - component: f7-card-content
      slots:
        default:
          - component: f7-list
            config:
              mediaList: true
            slots:
              default:
                - component: oh-list-item
                  config:
                    title: Restmüll
                    icon: f7:trash
                    iconColor: black
                    badge: '=((items.garbage_collection_restmuell.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_restmuell.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_restmuell.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_restmuell.displayState
                - component: oh-list-item
                  config:
                    title: Bioabfall
                    icon: f7:trash
                    iconColor: teal
                    badge: '=((items.garbage_collection_bioabfall.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_bioabfall.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_bioabfall.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_bioabfall.displayState
                - component: oh-list-item
                  config:
                    title: Gelbe Tonne
                    icon: f7:trash
                    iconColor: yellow
                    badge: '=((items.garbage_collection_gelb.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_gelb.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_gelb.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_gelb.displayState
                - component: oh-list-item
                  config:
                    title: Blaue Tonne
                    icon: f7:trash
                    iconColor: blue
                    badge: '=((items.garbage_collection_blue.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_blue.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_blue.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_blue.displayState
                - component: oh-list-item
                  config:
                    title: Sperrmüll
                    icon: f7:trash
                    iconColor: gray
                    badge: '=((items.garbage_collection_bulk.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_bulk.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_bulk.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_bulk.displayState
                - component: oh-list-item
                  config:
                    title: Weihnachtsbäume
                    icon: f7:trash
                    iconColor: green
                    badge: '=((items.garbage_collection_christmas.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_christmas.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_christmas.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_christmas.displayState
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '="Nächste Abholung: " + items.garbage_collection.displayState'
8 Likes

That’s really cool and exactly what I’m looking for (in the near future). I’m still in the process to migrate/rebuild/streamline all my OH2.5 stuff. I’m not yet in the layout stuff. But seems your sample will be my first learning and implementation.

I’m happy to provide feedback once I started.

Cheers
thefechner

Awesome! I will give it a try when my migration to OH3 is completed.
Thanks for sharing :grinning:

Thanks for sharing your thoughts on this. I just added the missing visibility configuration (when the next collection date is unknown the corresponding item will not be shown in the widget):

uid: garbage_cell_v4
tags: []
props:
  parameterGroups: []
timestamp: Jan 21, 2021, 11:21:27 AM
component: f7-card
config:
  title: ="Abfuhrtermine:"
slots:
  default:
    - component: f7-card-content
      slots:
        default:
          - component: f7-list
            config:
              mediaList: true
            slots:
              default:
                - component: oh-list-item
                  config:
                    title: Restmüll
                    icon: f7:trash
                    iconColor: black
                    badge: '=((items.garbage_collection_restmuell.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_restmuell.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_restmuell.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_restmuell.displayState
                    visible: '=items.garbage_collection_restmuell.state == "UNDEF" ? false : true'
                - component: oh-list-item
                  config:
                    title: Bioabfall
                    icon: f7:trash
                    iconColor: teal
                    badge: '=((items.garbage_collection_bioabfall.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_bioabfall.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_bioabfall.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_bioabfall.displayState
                    visible: '=items.garbage_collection_bioabfall.state == "UNDEF" ? false : true'
                - component: oh-list-item
                  config:
                    title: Gelbe Tonne
                    icon: f7:trash
                    iconColor: yellow
                    badge: '=((items.garbage_collection_gelb.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_gelb.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_gelb.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_gelb.displayState
                    visible: '=items.garbage_collection_gelb.state == "UNDEF" ? false : true'
                - component: oh-list-item
                  config:
                    title: Blaue Tonne
                    icon: f7:trash
                    iconColor: blue
                    badge: '=((items.garbage_collection_blue.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_blue.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_blue.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_blue.displayState
                    visible: '=items.garbage_collection_blue.state == "UNDEF" ? false : true'
                - component: oh-list-item
                  config:
                    title: Sperrmüll
                    icon: f7:trash
                    iconColor: gray
                    badge: '=((items.garbage_collection_bulk.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_bulk.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_bulk.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_bulk.displayState
                    visible: '=items.garbage_collection_bulk.state == "UNDEF" ? false : true'
                - component: oh-list-item
                  config:
                    title: Weihnachtsbäume
                    icon: f7:trash
                    iconColor: green
                    badge: '=((items.garbage_collection_christmas.displayState == items.local_dateweekday.displayState) ? "Heute" : (items.garbage_collection_christmas.displayState == items.local_dateweekday_tomorrow.displayState) ? "Morgen" : false)'
                    badgeColor: '=((items.garbage_collection_christmas.displayState == items.local_dateweekday.displayState) ? "red" : "yellow")'
                    footer: =items.garbage_collection_christmas.displayState
                    visible: '=items.garbage_collection_christmas.state == "UNDEF" ? false : true'
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '="Nächste Abholung: " + items.garbage_collection.displayState'

Since I really did not like the repetitions in the code and the inflexible usage of the code I improved it a bit today. It will still look the same but is now fully configurable so that it may be even used for any other type of events:

uid: garbage_list_v1
tags: []
props:
  parameters:
    - description: Title of the card
      label: Title
      name: title
      required: false
      type: TEXT
    - description: The card footer
      label: Footer
      name: footer
      required: false
      type: TEXT
    - description: Your local translation for <tomorrow>
      label: Tomorrow translation
      name: tomorrow
      required: true
      type: TEXT
    - description: Your local translation for <today>
      label: Today translation
      name: today
      required: true
      type: TEXT
    - description: Date items
      label: Date items
      name: datearray
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jan 26, 2021, 9:02:52 PM
component: f7-card
config:
  title: =props.title
slots:
  default:
    - component: f7-card-content
      slots:
        default:
          - component: f7-list
            config:
              mediaList: true
            slots:
              default:
                - component: oh-repeater
                  config:
                    for: listitem
                    in: =props.datearray.split("|")
                    fragment: true
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          title: =loop.listitem.split("\"")[1]
                          icon: =loop.listitem.split("\"")[5]
                          iconColor: =loop.listitem.split("\"")[3]
                          badge: '=((dayjs(items[loop.listitem.split("\"")[7]].state).diff(dayjs().startOf("day"), "days")) == 0 ? (props.today) : (dayjs(items[loop.listitem.split("\"")[7]].state).diff(dayjs().startOf("day"), "days")) == 1 ? (props.tomorrow) : false)'
                          badgeColor: '=((dayjs(items[loop.listitem.split("\"")[7]].state).diff(dayjs().startOf("day"), "days")) == 0 ? "red" : "yellow")'
                          footer: =items[loop.listitem.split("\"")[7]].displayState
                          visible: '=items[loop.listitem.split("\"")[7]].state == "UNDEF" ? false : true'
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: =props.footer

The only configuration option that requires a bit of explanation I guess is the “Date Items” property. I use the same format like the universal toggle widget (Universal Toggle Widget - #2 by DrRSatzteil) but with more parameters:
It requires a String in the following format:

"<Label 1>","<Color 1>","<Icon 1>","<Item Name 1>"|"<Label 2>","<Color 2>","<Icon 2>","<Item Name 2>"... and so on for more items

See here for available colors: Color Themes | Framework7 Documentation
See here for available icons: Framework7 Icons

1 Like

Thank you for this. It’s nice and helpful.
The next improvement could be sorting the list ascending from the nearest date :wink:

I already thought about that but I have no clue whether it could be done… It would be definitely nice but I don’t intend to go any deeper to investigate if it would be possible right now.

I use the following footer that tells me at least in how many days the next collection will take place. It’s not as good as a sorted list but good enough for me right now:

="Next collection: " + ((dayjs(items.garbage_collection.state).diff(dayjs().startOf("day"), "days")) == 0 ? "Today" : (dayjs(items.garbage_collection.state).diff(dayjs().startOf("day"), "days")) == 1 ? "Tomorrow" : "In " + (dayjs(items.garbage_collection.state).diff(dayjs().startOf("day"), "days")) + " days") 
1 Like

You could actually use an item representing the config string and calculate the string in a rule. Then use the value of this item in the widget config :grinning:

I would like to use your example. But am struggling with the setup. I am not very experienced but with a little nudge here and there I made it to setting most things up.

Could you explain what is needed to successfully see the content in the widget? Or in another words: Some granularity for “BTW: I read all the collection date values from my nextcloud calendar with the iCalendar binding.” would help.

I have a calendar bridge and the eventfilter thing. Text and schedule set up. Not created items from the channels yet. Now do I have to name the items in a special way? How does the widget know which calendar thing to display?

Hi Tim,

If you have the eventfilters already setup you’re almost there. You can create the date items with whatever name you like and connect them to the eventfilter channels. When you have all your items setup you can configure the widget via the Date Items property (make sure you are using the latest version of the widget I posted in this thread and not the one from my initial post):

The syntax is:

"<Label 1>","<Color 1>","<Icon 1>","<Item1>"|"<Label 2>","<Color 2>","<Icon 2>","<Item 2>"...

A concrete example would be:

"Waste","blue","f7:trash","name_of_my_waste_item"

Please let me know if that works for you!

understood, thanks. But I have to step back once again because it is also the first time I deal with the ical-binding: If I want to filter green, blue, grey and yellow collection date - do I create four eventfilter-things and the item to it?

In short: yes, exactly!

You could also retrieve all of them with one eventfilter but then it would be up to you to check what type of event you get on each channel. So I would recommend to do it exactly as you suggested.

Ok. Start with one EventFilter. Got three items: Biotonne_Start, Biotonne_Ende and Biotonne_Titel. all filled.
DateItem-String: “Waste”,“green”,“f7:trash”,“Biotonne_Titel”
The widget shows the green icon and “Biotonne”, but no dates?

Use the Biotonne_Start item. You don’t need the Titel. That should do the trick!

unfortunately it doesn’t. Now the widget displays the green icon and says “Waste”.
Ok, the “waste” is easy, changed it to “Biotonne”. But still no date.

I assume the date item shows the correct date?

2021-02-08T06:00:00.000+0100

could be that I have to transform or format the value? or configure the eventfilter in a different way?

Oh yes good point! You need to set the state description metadata for your item to make it work!

good, works now!
only your footer still shows NaN

If you want to use my footer you need to add all your garbage date items to a group with aggregation “earliest”.

Then use this group item as items.garbage_collection.state in my footer