Custom Widget: Calendar - 5 Upcoming Events

Tags: #<Tag:0x00007f1870a62a40> #<Tag:0x00007f1870a62900> #<Tag:0x00007f1870a626f8>

Displays up to 5 calendar events in HABPanel



Method 1:

Method 2:

  • Download calendar-five-events.widget.json
  • Import it using the HABPanel settings


  • Choose number of events that will be displayed in the widget (up to 5)
  • Select elements of the event (Date, Day, Summary, Time, Location)
  • Choose the highlight color (color of the date and summary elements)
  • Pick OpenHAB items for the events and elements you would like to display in the widget

The widget is located on my github page:
Calendar: 5 Upcoming Events

Since this is the first HABPanel widget I’ve made, if I’ve missed something, please, let me know.

Best regards,


Hi Davor @davorf

I really like your widget. The design is pretty well what I was looking for.
But I have one issue. Maybe I couldn’t find the right settings.

I have limited the events to 3. But when there are only 2 events at a given time loaded into openhab via the caldav binding, I see for the 3rd event all text positions as UNDEF.


Is there any way to handle this?



Since I have a lot of recurring events in my calendar, I can’t replicate this behavior. Actually, I’m not sure if I could replicate it (UNDEF item state), even if I delete all my events, because I’m not using CalDAV, but a Python script to collect Google Calendar events. What you could try, if you know how to edit a custom HABPanel widget, is adding ng-if to single event div section. Right now it looks like this:

<div class="row">
	<div class="col col-sm-2 left-column">
    <div class="row event-date">
    	<span ng-if="(config.CalendarGlobalShowDate)"> {{itemValue(config.CalendarEvent1StartDateTime) | date:'dd. MMM'}}</span>  

and it’s being repeated 5 times (for maximum of 5 events)

You can try editing first line of every event to something like this:

<div ng-if="(itemValue(config.CalendarEvent1StartDateTime) != 'UNDEF')" class="row">
	<div class="col col-sm-2 left-column">
    <div class="row event-date">
    	<span ng-if="(config.CalendarGlobalShowDate)"> {{itemValue(config.CalendarEvent1StartDateTime) | date:'dd. MMM'}}</span>  

So, the important part is:
<div ng-if="(itemValue(config.CalendarEvent1StartDateTime) != 'UNDEF')" class="row">

Of course, you should do that for all 5 sections, and change config.CalendarEvent1StartDateTime accordingly (Event1, for the first section, Event2 for the second etc.)

Hope this will help you find a way to tune widget to your liking.

Best regards,

1 Like

Hi @davorf would you mind providing your python script? I would like to display entries from my Google calendar too.



Of course. Here’s the topic with this script.

Best regards,

Thanks @davorf,

I now just have to tweak the layout a little bit to make the widget always look good independent of the number of shown events.


1 Like

I think your widget is great and would like to use it as well. How do I define the items to show the next 5 calendar entries, even if the next 5 are all on the same day.

Many greetings

Davor and Community,
I have prepared a version that looks simpler in number of changes that suppresses the UNDEF and NULL lines. Use it for your convinience:

<div class="calendar">
  <div ng-repeat-start="n in [].constructor(5) track by $index">
    <div ng-if="config.CalendarGlobalNumberOfEvents > $index" class="calendar-row" 
         ng-init="endTime =config['CalendarEvent'+ ($index+1)+'EndDateTime']; 
                  startTime=config['CalendarEvent'+ ($index+1)+'StartDateTime']; 
                  summary=config['CalendarEvent'+ ($index+1)+'Summary'];
                  location=config['CalendarEvent'+ ($index+1)+'Location'];">
      <div class="calendar-col-1" ng-if="itemValue(startTime)!='UNDEF' && itemValue(startTime)!='NULL'">
      	<div ng-if="(config.CalendarGlobalShowDate)" class="event-date"> {{itemValue(startTime) | date:'dd. MMM'}}</div>
      	<div ng-if="(config.CalendarGlobalShowDay)" class="event-day"> {{itemValue(startTime) | date:'EEE'}}</div>
      <div class="calendar-col-1" ng-if="itemValue(startTime)=='UNDEF' || itemValue(startTime)=='NULL'" ng-style="{opacity: 0}">
        <div ng-if="(config.CalendarGlobalShowDate)" class="event-date"> {{itemValue(startTime) | date:'dd. MMM'}}</div>
      	<div ng-if="(config.CalendarGlobalShowDay)" class="event-day"> {{itemValue(startTime) | date:'EEE'}}</div>
     	<div class="calendar-col-2">
        <div class="event-summary" ng-if="itemValue(summary)!='UNDEF' && itemValue(summary)!='NULL'">
      		<span ng-if="(config.CalendarGlobalShowSummary)"> {{itemValue(summary)}}</span>
        <div class="event-summary" ng-if="itemValue(summary)=='UNDEF' || itemValue(summary)=='NULL'" ng-style="{opacity: 0}">
      		<span ng-if="(config.CalendarGlobalShowSummary)"> {{itemValue(summary)}}</span>
        <div class="event-time">
          <span ng-if="(config.CalendarGlobalShowTime) && ((itemValue(startTime) | date:'dd.MM.yyyy') == (itemValue(endTime) | date:'dd.MM.yyyy'))"> {{itemValue(startTime) | date:'HH:mm'}}</span>
          <span ng-if="(config.CalendarGlobalShowTime) && ((itemValue(startTime) | date:'dd.MM.yyyy') == (itemValue(endTime) | date:'dd.MM.yyyy'))"> - </span>
          <span ng-if="(config.CalendarGlobalShowTime) && ((itemValue(startTime) | date:'dd.MM.yyyy') == (itemValue(endTime) | date:'dd.MM.yyyy'))"> {{itemValue(endTime) | date:'HH:mm'}}</span>
          <span ng-if="(config.CalendarGlobalShowTime) && ((itemValue(startTime) | date:'dd.MM.yyyy') < (itemValue(endTime) | date:'dd.MM.yyyy'))"> All day </span>      
        <div class="event-location" ng-if="itemValue(location)!='UNDEF' && itemValue(location)!='NULL'">
        	<span ng-if="(config.CalendarGlobalShowLocation)"> {{itemValue(location)}}</span>
        <div class="event-location" ng-if="itemValue(location)=='UNDEF' || itemValue(location)=='NULL'" ng-style="{opacity: 0}">
          <span ng-if="(config.CalendarGlobalShowLocation)"> {{itemValue(location)}}</span>
  <div ng-repeat-end ng-if="(config.CalendarGlobalNumberOfEvents > $index +1)">
      <hr class="event-divider"/>

Regards Werner

1 Like

Hi guys, I have installed this widget and I am very happy with it.
Maybe someone can explain me if it´s possible to add a title to this widget. (e.g. family calendar, personal calendar etc.)

Does it import from google calendar or something else?


thanks for this calendar widget.
I really like it.

However, I would like to tweak it to better fit my screen, but I failed.
I tried to create a stripped down version with just one entry.
So I deleted the entries at the bottom handling the drop down menues for 2,3,4 and 5.
This does not work though (empty screen), so I assume, that I need to delete / modify something in the beginning of the json file.

If anyone prepared a single entry calendar widget, I’d greatly appreciate if you could share is here.

You could modify a template widget I made here by removing the second and third table rows…

1 Like

I will check it out!

Here ya go in case your not so HTML handy. You can modify the CSS to fit your spot perfectly.


    tbody.calbody {
        height: 101%;
        display: block;
        position: absolute;
        width: 100%;
    } {
        width: 101%;

    td.calcal {
        background-color: #a9a9a9;
        padding: 17px;
        vertical-align: middle;
        width: 32%;
        text-align: center;
        height: 100%;

    td.calinfo {
        padding-top: 13px;
        width: 78%;
        vertical-align: top;
        padding-left: 16px;
    } {
        padding-bottom: 0px !important;
        color: #4c4c4c;
    } {
        padding-bottom: 0px;
        color: #212121;
    } {
        font-size: 30px;
        line-height: 30px;
        color: #212121;
    } {
        padding-bottom: 3px;
        font-size: 20px;
        font-weight: 400;
        line-height: 20px;
    } {
        padding-bottom: 0px;

    } {
        padding-bottom: 0px;


    tr.calentry {
        border-bottom: 5px solid #333333;
        height: 33.333%;
        width: 100%;
        max-width: 100%;
        display: flex;
        position: relative;


   <table class="caltable">
    <tbody class="calbody">
                <tr class="calentry">
                    <td class="calcal">
                        <div class="calday">{{itemValue('CalendarTimeStart1')| date:"EEE"}}</div>
                        <div class="calmonth">{{itemValue('CalendarTimeStart1')| date:"MMM"}}</div>
                        <div class="caldaynumber">{{itemValue('CalendarTimeStart1')| date:"dd"}}</div>
                    <td class="calinfo"><div class="calname">{{itemValue('CalendarName1')}}</div>
                        <div ng-if="((itemValue('CalendarTimeStart1') | date:'dd.MM.yyyy') < (itemValue('CalendarTimeEnd1') | date:'dd.MM.yyyy'))" class="calduration">All Day</div>
                        <div ng-if="((itemValue('CalendarTimeStart1') | date:'dd.MM.yyyy') == (itemValue('CalendarTimeEnd1') | date:'dd.MM.yyyy'))" class="calduration">{{itemValue('CalendarTimeStart1')| date:"shortTime"}} - {{itemValue('CalendarTimeEnd1')| date:"shortTime"}}</div>
                        <div class="calloc">{{itemValue('CalendarAt1')}}</div>
1 Like

Thanks a lot for your help!
I got it!
Only one thing does not work yet:
<div ng-if="((itemValue('CalStart9') | date:'dd.MM.yyyy') < (itemValue('CalEnd9') | date:'dd.MM.yyyy'))" class="calduration">All day</div>

Does this work in your setup?

I think I found the root cause.
It seems to be because the entry (vacation) exceeds the end of the year!?

BUT: Shouldn’t this still be working?

Hmm, yeah I mean it should work but to be honest I haven’t spent a lot of time trying all the variations of multiple day events, or events that cross into the next year.

Is it just the start and end times that are not showing?

Actually it should show “All day” if the entry is set to a full day.
My appointments are actually set to midnight as the “all day” meetings in outlook are created (move them over to icloud afterwards).

So this is the result for “all day” entries:
Dec 25th to Dec 28th -> All Day (works as expected)
Dec 25th to Jan 2nd -> no entry (not NULL / UNDEF - just nothing)
Dec 25th to Dec 31st -> no entry like case 2

Because the 3rd case ends on 31st at midnight, I assume that that’s the magic point.

However, I don’t get it, when the following is not true:
ng-if="((itemValue('CalStart9') | date:'dd.MM.yyyy') < (itemValue('CalEnd9') | date:'dd.MM.yyyy'))

e.g. in the 2nd case 25.12.2019 shold be smaller than 02.01.2020, because the year is taken into account.

shouldn’t it?

Yeah it should be working, I noticed a 3 day event I have is also not showing anything. It won’t show undef of Null as the ng-if will only show something if it is a true statement. If it doesn’t match the if statement it will just not show anything. That tells me that somehow the logic is not actually registering the earlier date as an earlier date or less than the former. Need someone more knowledgeable than me to work this one out…

I tried to check if the start time is 00:00, but wasn’t successful checking a few syntax’

Any suggestion how this should look like?
<div ng-if="((itemValue('CalStart14') | date:'HH:MM') == '00:00')" class="calduration">{{itemValue('CalStart14')| date:"dd.MM."}} - {{itemValue('CalEnd14')| date:"dd.MM."}}</div>