DIY Family Calendar (Skylight)

Thanks works!

1 Like

what do i need to edit to make it show correctly on a smaller screen?
a 10" tablet for example.

A screen shot might help to explain your issue. The configuration includes dynamic sizing based on screen resolution.

Where do I find the dynamic sizing, this is what I want to edit.

It’s in dashboard.yaml.

I thought so but I dont know what to edit, can you point me at instructions or tell me how? please.

It’s this bit:

          DAYS: |
            (() => {
              const width = window.innerWidth;
              const calendarView = states['input_select.calendar_view']?.state;
              if (width >= 390 && width<= 500) return 1;
              if (calendarView === 'Today') return 1;
              if (calendarView === 'Tomorrow') return 2;
              if (calendarView === 'Bimonth' && width >= 1920) return 56;
              if (calendarView === 'Month' && width >= 1920) return 28;
              if (calendarView === 'Biweek' && width >= 1920) return 14;
              if (calendarView === 'Week' && width >= 1920) return 7;
              return 3;
            })()

It’s using the resolution width in pixels to decide how many days to display, depending on the chosen view.

I never really tinkered with it and I don’t think there are any instructions on the OP’s Github.

I’m actually not using this implementation now. I have swapped to this custom card: Skylight Calendar Card — A family-friendly schedule card.

Hi @JoeMo22,
Were you able to resolve the ā€œpatternWarningā€ error. If yes, what was the solution that you used?

Thanks!

Are you both using the Visual Studio Code Server addon (app) to edit your configuration.yaml? If so, you can ignore the pattern warning error.

Otherwise, the entry should look like this:

homeassistant: 
  packages: !include_dir_named packages

If you don’t have homeassistant:, just create it.

Thanks for replying @Orange-GT3.

I am using the VS Code Server addon.
Forgive an inexperienced user question, but when you say create homeassistant:, what do you mean?
I’ve copy pasted this into config.yaml:

homeassistant:
  packages: !include_dir_named packages

I’ve created a packages folder, in the Config folder.

What you have there is correct.

Good, however dashboard.yaml does not go in that folder, family-calendar.yaml does.

You use dashboard.yaml as the basis for a new dashboard to display your calendar.

1 Like

Hi

Can anyone help me I’m pulling my hair out.

This is my dashboard code but i cannot see the actual weeks planner section.

I’ve tried all sorts but keep getting to a dead end. I’ve even tried gemeni to help me out but no luck

views:
  - type: sections
    max_columns: 10
    title: Family Calendar
    path: family-calendar
    theme: Skylight
    sections:
      - type: grid
        cards:
          - type: custom:better-moment-card
            parentStyle: |
              line-height:normal;
            moment:
              - parentStyle: |
                  font-size:1em; text-align:center; margin-top:5px;
                templateRaw: |
                  {{moment format=cccc}}
              - parentStyle: |
                  font-size:1.5em; text-align:center; margin-top:5px;
                templateRaw: |
                  {{moment format=LLLL dd, yyyy}}              
              - parentStyle: |
                  font-size:4em;  text-align:center; font-weight:400;
                templateRaw: |
                  {{moment format=HH:mm}} 
            grid_options:
              columns: 20
            card_mod:
              style: |
                ha-card {
                  background: transparent !important;
                  box-shadow: none !important;
                  border: none !important;
                }
          - type: custom:weather-card
            entity: weather.forecast_home
            current: true
            details: true
            forecast: false
            grid_options:
              columns: 20
              rows: 3
          - type: weather-forecast
            show_current: false
            show_forecast: true
            entity: weather.forecast_home
            forecast_type: daily
            name: Weather Forecast
            grid_options:
              columns: 20
              rows: 3
            card_mod:
              style: |
                ha-card {
                  background: transparent !important;
                  box-shadow: none !important;
                  border: none !important;
                }
        column_span: 10
      - type: grid
        cards:
          - type: vertical-stack
            cards:
              - type: markdown
                content: ' '
                card_mod:
                  style: >
                    ha-card { background: none; box-shadow: none; border: none;
                    }
                grid_options:
                  columns: 18
                  rows: auto
            grid_options:
              columns: 120
              rows: auto
          - type: vertical-stack
            cards:
              - type: markdown
                content: <font color="Black" size="6">Family Calendar</font>
                grid_options:
                  columns: 18
                  rows: auto
            grid_options:
              columns: 120
              rows: auto
          - type: horizontal-stack
            cards:
              - type: custom:bubble-card
                card_type: button
                button_type: name
                entity: calendar.finley
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.calendar1_calendar_visible_filter
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.calendar1_calendar_filter'].state === '.*' ? 'light-grey' : '#ff7f00'} !important;
                  }
                sub_button:
                  main: []
                  bottom: []
                slider_fill_orientation: left
                slider_value_position: right
                name: Finley
              - type: custom:bubble-card
                card_type: button
                button_type: name
                entity: calendar.winnie
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.calendar2_calendar_visible_filter
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.calendar2_calendar_filter'].state === '.*' ? 'light-grey' : '#ff7f00'} !important;
                  }
                sub_button:
                  main: []
                  bottom: []
                slider_fill_orientation: left
                slider_value_position: right
                name: Winnie
              - type: custom:bubble-card
                card_type: button
                button_type: name
                entity: calendar.jodi
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.calendar3_calendar_visible_filter
                  target: {}
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.calendar4_calendar_filter'].state === '.*' ? 'light-grey' : 'var(--calendar4-default-primary-color)'} !important;
                  }
                sub_button:
                  main: []
                  bottom: []
                slider_fill_orientation: left
                slider_value_position: right
                name: Jodi
              - type: custom:bubble-card
                card_type: button
                button_type: name
                entity: calendar.tom
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.calendar4_calendar_visible_filter
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.calendar4_calendar_filter'].state === '.*' ? 'light-grey' : 'var(--calendar4-default-primary-color)'} !important;
                  }
                sub_button:
                  main: []
                  bottom: []
                slider_fill_orientation: left
                slider_value_position: right
                name: Tom
              - type: custom:bubble-card
                card_type: button
                button_type: switch
                name: Family
                icon: mdi:human-male-female-child
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.family_calendar_visible_filter
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.family_calendar_filter'].state === '.*' ? 'light-grey' : '#4A90E2'} !important;
                  }
                entity: input_boolean.family_calendar_show
              - type: custom:bubble-card
                card_type: button
                button_type: name
                name: Birthdays
                icon: mdi:cake-variant
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.birthdays_calendar_visible_filter
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.birthdays_calendar_filter'].state === '.*' ? 'light-grey' : '#33a02c'} !important;
                  }
              - type: custom:bubble-card
                card_type: button
                button_type: name
                name: Holidays
                icon: mdi:bag-personal
                show_icon: true
                show_name: true
                tap_action:
                  action: perform-action
                  perform_action: script.holidays_calendar_visible_filter
                styles: |
                  .bubble-button-background {
                    opacity: 1 !important;
                    background-color: ${hass.states['input_text.holidays_calendar_filter'].state === '.*' ? 'light-grey' : '#ff7f00'} !important;
                  }
                sub_button:
                  main: []
                  bottom: []
                slider_fill_orientation: left
                slider_value_position: right
            grid_options:
              columns: 45
              rows: auto
          - type: markdown
            content: ' '
            grid_options:
              columns: 3
              rows: auto
          - type: custom:bubble-card
            card_type: button
            button_type: name
            card_layout: large
            name: Add Event
            icon: mdi:calendar-plus
            tap_action:
              action: navigate
              navigation_path: '#addcalendarevent'
            styles: >
              * { font-size: 1.05em !important; }

              ha-card { --bubble-main-background-color: #393745 !important;
              width: 300px; }    

              .bubble-icon { --mdc-icon-size: 30px !important; color: snow
              !important; opacity: 1; }

              .bubble-icon-container { background: #393745 !important; display:
              flex; }

              .bubble-name { color: snow !important; opacity: 1; display: flex;
              line-height: 18px; flex-direction: row; justify-content: center;
              flex-grow: 1; margin: 0 40px 0 0; pointer-events: none; position:
              relative; overflow: hidden; }
            grid_options:
              columns: 10
              rows: 1
          - type: custom:bubble-card
            card_type: select
            entity: input_select.calendar_view
            show_name: true
            show_state: true
            name: Select View
            show_last_changed: false
            show_attribute: false
        column_span: 10
      - type: grid
        cards:
          - type: custom:config-template-card
            entities:
              - input_text.calendar1_calendar_filter
              - input_text.calendar2_calendar_filter
              - input_text.calendar3_calendar_filter
              - input_text.calendar4_calendar_filter
              - input_text.family_calendar_filter
              - input_text.holidays_calendar_filter
              - input_text.birthdays_calendar_filter
              - input_select.calendar_view
            variables:
              CAL1CAL: states['input_text.calendar1_calendar_filter']?.state
              CAL2CAL: states['input_text.calendar2_calendar_filter']?.state
              CAL3CAL: states['input_text.calendar3_calendar_filter']?.state
              CAL4CAL: states['input_text.calendar4_calendar_filter']?.state
              FAMCAL: states['input_text.family_calendar_filter']?.state
              HOLCAL: states['input_text.holidays_calendar_filter']?.state
              BIRCAL: states['input_text.birthdays_calendar_filter']?.state
              VIEW: states['input_select.calendar_view']?.state
              STARTDAY: |
                (() => {
                  const calendarView = states['input_select.calendar_view']?.state;
                  if (calendarView === 'Today') return 'today';
                  if (calendarView === 'Tomorrow') return 'tomorrow';
                  // Fallback
                  return 'monday'; # <--- UPDATE THIS IF NEEDED, valid values are: monday, tuesday, wednesday, thursday, friday, saturday, sunday
                })()
              DAYS: |
                (() => {
                  const calendarView = states['input_select.calendar_view']?.state;
                  if (calendarView === 'Today') return 1;
                  if (calendarView === 'Tomorrow') return 2;
                  if (calendarView === 'Week') return 7;
                  if (calendarView === 'Biweek') return 14;
                  // else return "month"
                  return "month";
                })()
            card:
              type: custom:week-planner-card
              calendars:
                - entity: calendar.calendar1
                  name: calendar1
                  color: var(--calendar1-default-primary-color)
                  filter: ${ CAL1CAL }
                - entity: calendar.calendar2
                  name: calendar2
                  color: var(--calendar2-default-primary-color)
                  filter: ${ CAL2CAL }
                - entity: calendar.calendar3
                  name: calendar3
                  color: var(--calendar3-default-primary-color)
                  filter: ${ CAL3CAL }
                - entity: calendar.calendar4
                  name: calendar4
                  color: var(--calendar4-default-primary-color)
                  filter: ${ CAL4CAL }
                - entity: calendar.family
                  name: Family
                  color: var(--family-default-primary-color)
                  filter: ${ FAMCAL }
                - entity: calendar.birthdays
                  name: Birthdays
                  color: var(--birthdays-default-primary-color)
                  filter: ${ BIRCAL }
                - entity: calendar.holidays
                  name: Holidays
                  color: var(--holidays-default-primary-color)
                  filter: ${ HOLCAL }
              days: ${ DAYS }
              startingDay: ${ STARTDAY }
              showNavigation: true
              showWeekDayText: false
              startingDayOffset: 0
              hideWeekend: false
              noCardBackground: false
              compact: false
              weather:
                showCondition: true
                showTemperature: true
                showLowTemperature: true
                useTwiceDaily: false
                entity: weather.home
              locale: en
              showLocation: true
              hidePastEvents: false
              hideDaysWithoutEvents: false
              hideTodayWithoutEvents: false
              combineSimilarEvents: true
              showLegend: false
              legendToggle: false
              card_mod:
                style: >
                  /* === BASE SAFETY === */

                  :host {
                    overflow: hidden;
                  }


                  /* === CARD BASE === */

                  ha-card {
                    background: rgba(255, 255, 255, 0.6) !important;
                    border-radius: 24px !important;
                    box-shadow: none !important;
                    height: clamp(480px, 70vh, 900px) !important;
                    max-height: clamp(480px, 70vh, 900px) !important;
                  }


                  /* === GRID LAYOUT === */

                  .container {
                    display: grid !important;
                    grid-template-columns: repeat(7, minmax(0, 1fr)) !important;
                    grid-auto-flow: row dense !important;
                    gap: 0 !important;
                  }


                  .container .navigation,

                  .container .header {
                    grid-column: 1 / -1 !important;
                  }


                  /* WEEKDAY LABEL CELLS */

                  .day.header {
                    display: block !important;
                    grid-column: auto !important;
                    margin: 0 !important;
                    padding: 0.2em !important;
                    text-align: center !important;
                    box-sizing: border-box !important;
                  }


                  .day.header .date .text {
                    font-weight: 600 !important;
                  }


                  /* === DAY TILES === */

                  .day {
                    border: solid 1px whitesmoke !important;
                    padding: 0.2% !important;
                    width: auto !important;
                    min-width: 0 !important;
                    margin: 0 !important;
                    box-sizing: border-box !important;
                    align-self: stretch !important;
                    justify-self: stretch !important;
                  }


                  /* === YOUR ORIGINAL VISUAL RULES === */

                  .event.past {
                    opacity: .2 !important;
                    background-color: gray !important;
                  }


                  .time {
                    color: #333333 !important;
                    font-size: 0.8em !important;
                  }


                  .event {
                    color: #333333 !important;
                    line-height: 16px !important;
                    background-color: var(--border-color) !important;
                    border-radius: 10px !important;
                    max-height: 80px !important;
                    overflow: hidden !important;
                    font-size: 1.1em !important;
                  }


                  .none {
                    background-color: transparent !important;
                  }


                  .today .number {
                    border-radius: 5px !important;
                    background-color: orange !important;
                    padding-left: 4px !important;
                    padding-right: 4px !important;
                  }


                  .day .date .text {
                    font-size: 1em !important;
                    font-weight: bold !important;
                  }


                  .day .date .number {
                    font-weight: bold !important;
                    font-size: 3em !important;
                  }


                  /* Weekday colors */


                  /* When showWeekDayText is true */

                  .day[data-weekday="1"] .date .text,

                  .day[data-weekday="2"] .date .text,

                  .day[data-weekday="3"] .date .text,

                  .day[data-weekday="4"] .date .text,

                  .day[data-weekday="5"] .date .text {
                    color: #2e7d32 !important;
                  }


                  .day[data-weekday="6"] .date .text,

                  .day[data-weekday="7"] .date .text {
                    color: #d32f2f !important;
                  }

                  /* When showWeekDayText is false */

                  .container > .day.header .text {
                    color: #2e7d32 !important;
                  }

                  /* Saturday = column 7 */

                  .container > .day.header:nth-of-type(7) .text {
                    color: #d32f2f !important;
                  }

                  /* Sunday = column 8 */

                  .container > .day.header:nth-of-type(8) .text {
                    color: #d32f2f !important;
                  }



                  /* === RESPONSIVE BREAKPOINTS === */

                  @media (max-width: 1024px) {
                    .day .date .number { font-size: 2em !important; }
                    .day .date .text   { font-size: 0.95em !important; }
                  }


                  @media (max-width: 768px) {
                    .day .date .number { font-size: 1.6em !important; }
                    .day .date .text   { font-size: 0.85em !important; }
                    .event { font-size: 0.85em !important; line-height: 1.1em !important; }
                    .time  { font-size: 0.75em !important; }
                  }


                  /* =========================================================
                  */

                  /* === MOBILE OVERLAP FIX + TWO‑LINE WRAP FOR TITLES ========
                  */

                  /* =========================================================
                  */

                  @media (max-width: 480px) {

                    :host {
                      font-size: clamp(10px, 2.6vw, 13px) !important;
                    }

                    /* Condensed headers */
                    .container .header,
                    .container .navigation {
                      padding: 0.25em 0.4em !important;
                    }

                    /* Day tiles tighter */
                    .day {
                      padding: 0.15em !important;
                      min-width: 0 !important;
                    }

                    /* Smaller date numbers */
                    .day .date .number {
                      font-size: clamp(1.1em, 4.5vw, 1.4em) !important;
                      line-height: 1.1 !important;
                    }
                    .day .date .text {
                      font-size: clamp(0.72em, 3.2vw, 0.85em) !important;
                      line-height: 1.1 !important;
                    }

                    /* Event chip reduced */
                    .event {
                      font-size: clamp(0.72em, 2.9vw, 0.85em) !important;
                      line-height: 1.05em !important;
                      padding: 0.25em 0.35em !important;
                      max-height: 62px !important;
                    }

                    .time {
                      font-size: clamp(0.65em, 2.5vw, 0.78em) !important;
                    }

                    /* === TWO-LINE WRAP FOR TITLES === */
                    .event .title,
                    .event .name,
                    .event .summary,
                    .event .location,
                    .event .desc,
                    .event .description {
                      display: -webkit-box !important;
                      -webkit-box-orient: vertical !important;
                      -webkit-line-clamp: 2 !important;   /* <<< Two lines max */
                      overflow: auto !important;
                      text-overflow: ellipsis !important;
                      white-space: normal !important;
                      line-height: 1.1em !important;
                      max-height: calc(1.1em * 2) !important;
                    }
                  }


                  /* Slightly larger phones / small tablets */

                  @media (min-width: 481px) and (max-width: 768px) {

                    :host {
                      font-size: clamp(11px, 2.2vw, 14px) !important;
                    }

                    .event {
                      max-height: 72px !important;
                    }

                    .event .title,
                    .event .name,
                    .event .summary {
                      display: -webkit-box !important;
                      -webkit-line-clamp: 2 !important;
                      overflow: auto !important;
                    }
                  }
            grid_options:
              columns: 120
              rows: 1
              showDescription: false
        column_span: 10
      - type: grid
        cards:
          - type: vertical-stack
            cards:
              - type: custom:bubble-card
                card_type: pop-up
                hash: '#addcalendarevent'
                button_type: name
                name: Add Calendar Event
                icon: mdi:calendar-plus
                show_icon: true
                show_name: true
                styles: |
                  .bubble-button-card-container {
                    background: 
                    ${hass.states['input_select.calendar_select'].state == 'calendar1' ? 'var(--calendar1-default-primary-color)' 
                    : hass.states['input_select.calendar_select'].state == 'calendar2' ? 'var(--calendar2-default-primary-color)' 
                    : hass.states['input_select.calendar_select'].state == 'calendar3' ? 'var(--calendar3-default-primary-color)' 
                    : hass.states['input_select.calendar_select'].state == 'calendar4' ? 'var(--calendar4-default-primary-color)' 
                    : hass.states['input_select.calendar_select'].state == 'Family' ? 'var(--family-default-primary-color)' 
                    : 'gray'} !important;
                  }
              - type: vertical-stack
                cards:
                  - type: entities
                    entities:
                      - entity: input_select.calendar_select
                      - entity: input_text.calendar_event_title
                        name: Event Title
                      - entity: input_text.calendar_event_description
                        name: Event Description
                      - entity: input_boolean.calendar_all_day_event
                        name: All Day Event
                    title: Add Calendar Event
                    state_color: false
                  - type: conditional
                    conditions:
                      - entity: input_boolean.calendar_all_day_event
                        state: 'off'
                    card:
                      type: entities
                      entities:
                        - entity: input_datetime.calendar_event_start
                          name: Start Time
                        - entity: input_datetime.calendar_event_end
                          name: End Time
                  - type: conditional
                    conditions:
                      - entity: input_boolean.calendar_all_day_event
                        state: 'on'
                    card:
                      type: entities
                      entities:
                        - entity: input_datetime.calendar_day_event_start
                          name: Event Start Date
                        - entity: input_datetime.calendar_day_event_end
                          name: Event End Date
                  - type: custom:button-card
                    name: Add Event to Calendar
                    tap_action:
                      action: call-service
                      service: script.add_google_calendar_event
                    styles:
                      card:
                        - background-color: |
                            [[[
                              if (states['input_select.calendar_select'].state == 'calendar1') return "var(--calendar1-default-primary-color)";
                              if (states['input_select.calendar_select'].state == 'calendar2') return "var(--calendar2-default-primary-color)";
                              if (states['input_select.calendar_select'].state == 'calendar3') return "var(--calendar3-default-primary-color)";
                              if (states['input_select.calendar_select'].state == 'calendar4') return "var(--calendar4-default-primary-color)";
                              if (states['input_select.calendar_select'].state == 'Family') return "var(--family-default-primary-color)";
                              return "gray";
                            ]]]                
    cards: []
    icon: mdi:calendar-blank-multiple
    background:
      opacity: 62
      alignment: center
      size: cover
      repeat: repeat
      attachment: fixed
    top_margin: false

This is what I see

Im just trying to get everything fixed before i worry about sizing and themes

THanks

Replace this line

return 'monday'; # <--- UPDATE THIS IF NEEDED, valid values are: monday, tuesday, wednesday, thursday, friday, saturday, sunday

with this and it will start working

return 'monday'; // <--- UPDATE THIS IF NEEDED, valid values are: monday, tuesday, wednesday, thursday, friday, saturday, sunday
1 Like

Omg a8t last why is that soncomplex

Now I just have a dam spinning circle

Any ideas

Check that you updated the weather entity in dashboard.yaml.

There is an alternative to this that is much easier to configure.

I’m confused what you mean as all my weather cards are working ok

There is a weather entity in the calendar card config that you need to update.

HA is throwing an error anytime I try to add an all-day event that is a single day
Error is ā€œFailed to perform the action script/add_google_calendar_event. Expected minimum event duration of 0:00:01 (2026-02-21, 2026-02-21)ā€

It’s odd because the error is referencing a Google Calendar event, but I am trying to add this to a local calendar.

Any guidance on a resolution for this?

Never mind. I just solved this myself. It’s because of ICS-Calendar-Tools, where you have to set the end date of the event as the next day. So if I want an all day on 2/21, I set start for 2/21 and end for 2/22, and then it shows the all day for just 2/21…

1 Like

Hi Mate,
First of all - such an amazing job.
After many problems i finally got it working :smiley:
Just wanted to ask - how’s it going with chores? :smiley:
Could You share the code for it?

Greetings from Poland :slight_smile: