OH3 - Expandable F7-card

Hi Stefan,

what i see in your widget is that you have placed all content with “absolute” positioning, everything what goes in expanded is nicely in rows and colums. i believe this is the root cause. This does not explain why the position absolute is disapearing, but as a test if you just remove the absolute element from the gras, it obviously will be re-positioned like everything else ( as absolute is related to each other ), but if you do the test then with expanding and colapsing, the gras is still there where it was. I believe to sort the problem, you need to work in rows and colums to properly position the elements within the card-content part of the widget unless someone has another solution.

There is a nice discussion in another post around this topic of placement, this might eventually help:

Hi Jan,

the absolute position is already a workaround I use.
Initially I used a f7-col with flex-end alignment.
This was not working either, as the width of the card is not correct once collapsed.

EDIT: the position is correct, when I move the icon in the content-header.
However in this case the icon length is stretched -same as the close icon in point 3) in the initial question.

looping in @RGroll who might have an idea of what is going on with this? Sorry Rainer to put you on the spot :slight_smile:

Hey @stefan13 and @muelli1967

first of all, nice widget!


This behavior is mentioned in the docs here.

Expandable card content (card-content ) is set to 100vw width when collapsed (closed). It is done to improve card open/close transition performance, so you need to take care about its content positioning. You can make its content width also animatable and responsive by adding additional card-expandable-animate-width class to card element, but performance can be worse in this case.

The 100vw width will overwrite the applied styling on the card-content component after the first expansion of the card. Performance seems okay for me after adding the mentioned class, which would be the easiest soloution for your case, I think.


The expand transformation will applied for each element outside of card content-elements (card-header, card-content, card-footer) which leads to the stretched icon - so it’s needed that any visible element for the opened card is inside these card content-elements.


I had a quick look at your widget and done some small optimizations to the mentioned problems - but only for the ones that were obvious at a first glance, so its worth checking all components again.

expandable_card

YAML
uid: widget_Bewaesserung_v1-3
component: f7-card
config:
  expandable: true
  swipeToClose: true
  backdrop: true
  class:
    - card-expandable-animate-width
  style:
    height: 200px
    width: auto
slots:
  default:
    - component: oh-button
      config:
        iconF7: gear
        iconSize: 30px
        color: black
        style:
          position: absolute
          top: 0
          right: 0
          padding-top: 10px
          padding-bottom: 35px
          z-index: 999
        class:
          - cell-open-button
          - card-opened-fade-out
    - component: f7-card-content
      config:
        style:
          width: 100%
      slots:
        default:
          - component: f7-icon
            config:
              material: grass
              size: 80px
              color: green
              style:
                opacity: 40%
                position: absolute
                top: 120px
                right: 16px
          - component: oh-button
            config:
              iconF7: xmark_circle_fill
              iconSize: 30px
              color: black
              style:
                position: absolute
                top: 0
                right: 0
                padding-top: 10px
                padding-bottom: 35px
                z-index: 999
              class:
                - card-opened-fade-in
                - cell-close-button
                - card-close
          - component: oh-link
            config:
              action: toggle
              actionItem: Bewaesserung_Rasen_Schalter
              actionCommand: ON
              actionCommandAlt: OFF
              class:
                - card-prevent-open
              style:
                width: 100%
                height: 100%
                position: absolute
                top: 0
                left: 0
                z-index: 0
          - component: f7-block
            config:
              class:
                - no-padding
              style:
                margin: 0px
                height: 200px
            slots:
              default:
                - component: f7-row
                  config:
                    style:
                      height: 75px
                      white-space: nowrap
                      flex-wrap: nowrap
                  slots:
                    default:
                      - component: f7-col
                        slots:
                          default:
                            - component: Label
                              config:
                                text: Büsche hinten
                                style:
                                  font-size: 25px
                                  font-weight: 600
                                  text-overflow: ellipsis
                                  overflow: hidden
                                  white-space: nowrap
                            - component: f7-chip
                              config:
                                text: "=items.Bewaesserung_Rasen_Schalter.state === 'ON' ? 'Bewässerung aktiv' : 'AUS'"
                                color: "=items.Bewaesserung_Rasen_Schalter.state === 'ON' ? 'green' : 'gray'"
                      - component: f7-col
                        config:
                          style:
                            width: auto
                        slots:
                          default:
                            - component: oh-gauge
                              config:
                                min: 0
                                max: 90
                                type: semicircle
                                value: 40
                                labelText: 20 min
                                borderWidth: 20
                                size: 100
                                borderBgColor: "#d2d2d2"
                                borderColor: "#4287f5"
                                visible: "=items.Bewaesserung_Rasen_Schalter.state === 'ON' ? true : false"
                                style:
                                  margin-right: 15px
                                  margin-top: 15px
  
                - component: f7-row
                  config:
                    style:
                      white-space: nowrap
                      flex-wrap: nowrap
                      height: 30px
                    class:
                      - justify-content-flex-start
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: cloud_rain
                          size: 20px
                          color: gray
                          visible: "=items.Bewaesserung_Rasen_Regenautomatik.state === 'ON' ? true : false"
                          style:
                            margin-right: 5px
                      - component: f7-icon
                        config:
                          f7: umbrella
                          size: 20px
                          color: gray
                          visible: "=Number.parseInt(items.item_Netatmo_Regen_Niederschlag.displayState) >= 1 ? true : false"
                          style:
                            margin-right: 5px
                - component: f7-row
                  config:
                    style:
                      height: 66px
                      width: 100%
                      overflow: hidden
                  slots:
                    default:
                      - component: f7-col
                        config:
                          style:
                            flex-wrap: nowrap
                            align-self: flex-end
                        slots:
                          default:
                            - component: f7-row
                              config:
                                class:
                                  - justify-content-flex-start
                                style:
                                  flex-wrap: nowrap
                                  width: 100%
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      f7: alarm
                                      size: 20px
                                      color: blue
                                      visible: "=items.Bewaesserung_Rasen_Timer.state === 'ON' ? true : false"
                                      style:
                                        margin-top: 0px
                                  - component: Label
                                    config:
                                      text: 19. April, 18:30
                                      visible: "=items.Bewaesserung_Rasen_Timer.state === 'ON' ? true : false"
                                      style:
                                        text-overflow: ellipsis
                                        overflow: hidden
                                        white-space: nowrap
                                        margin-left: 5px
                                        font-size: 14px
                                        font-weight: 600
                                  - component: f7-icon
                                    config:
                                      f7: timer
                                      size: 20px
                                      color: blue
                                      visible: "=items.Bewaesserung_Rasen_Timer.state === 'ON' ? true : false"
                                      style:
                                        margin-top: 0px
                                        margin-left: 10px
                                  - component: Label
                                    config:
                                      text: 35min
                                      visible: "=items.Bewaesserung_Rasen_Timer.state === 'ON' ? true : false"
                                      style:
                                        text-overflow: ellipsis
                                        overflow: hidden
                                        white-space: nowrap
                                        margin-left: 5px
                                        font-size: 14px
                                        font-weight: 600
                            - component: f7-row
                              config:
                                class:
                                  - justify-content-flex-start
                                style:
                                  margin-top: 5px
                                  flex-wrap: nowrap
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      material: restore
                                      color: gray
                                      size: 18px
                                      style:
                                        margin-top: 0px
                                        margin-left: 0px
                                  - component: Label
                                    config:
                                      text: 14. April, 18:30
                                      style:
                                        margin-left: 8px
                                        font-size: 12px
                                        color: gray
                                        text-overflow: ellipsis
                                        overflow: hidden
                                        white-space: nowrap
                                  - component: f7-icon
                                    config:
                                      f7: timer
                                      size: 15px
                                      color: gray
                                      style:
                                        margin-top: 2px
                                        margin-left: 10px
                                  - component: Label
                                    config:
                                      text: 20min
                                      style:
                                        margin-left: 3px
                                        font-size: 12px
                                        color: gray
                                        text-overflow: ellipsis
                                        overflow: hidden
                                        white-space: nowrap
          - component: f7-block
            config:
              class:
                - card-prevent-open
                - card-content-padding
              style:
                height: 300px
            slots:
              default:
                - component: oh-list
                  config:
                    class:
                      - padding
                  slots:
                    default:
                      - component: oh-stepper-item
                        config:
                          title: Ausschalt Timer (min)
                          item: Bewaesserung_Rasen_Ausschalt_Timer
                          icon: f7:timer
                          color: blue
                          raised: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: false
                          min: 0
                          max: 90
                      - component: Label
                        config:
                          text: =props.Title?props.Title:"Timer Settings"
                          style:
                            padding: 7px
                            border-bottom: 1px solid grey
                            font-weight: 600
                            color: gray
                            margin-top: 10px
                      - component: oh-toggle-item
                        config:
                          title: Timer
                          icon: f7:alarm
                          color: green
                          item: Bewaesserung_Rasen_Timer
                      - component: oh-input-item
                        config:
                          title: Startzeit
                          type: datepicker
                          sendButton: true
                          item: Bewaesserung_Rasen_Timer_Startzeit
                          outline: false
                          calendarParams:
                            timePicker: true
                            dateFormat:
                              month: short
                              day: numeric
                              hour: numeric
                              minute: numeric
                          style:
                            --f7-input-bg-color: "#ededed"
                      - component: oh-stepper-item
                        config:
                          item: Bewaesserung_Rasen_Timer_Laufzeit
                          title: Laufzeit (min)
                          icon: f7:timer
                          color: blue
                          raised: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: false
                          min: 0
                          max: 90
                      - component: oh-stepper-item
                        config:
                          item: Bewaesserung_Rasen_Interval
                          title: Interval (Tage)
                          icon: f7:arrow_2_circlepath
                          color: blue
                          raised: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: false
                          min: 0
                          max: 90
                      - component: Label
                        config:
                          text: =props.Title?props.Title:"Wettersteuerung"
                          style:
                            padding: 7px
                            border-bottom: 1px solid grey
                            font-weight: 600
                            color: gray
                            margin-top: 10px
                      - component: oh-toggle-item
                        config:
                          title: Regenautomatik
                          icon: f7:cloud_rain
                          color: green
                          item: Bewaesserung_Rasen_Regenautomatik
                      - component: oh-stepper-item
                        config:
                          item: Bewaesserung_Rasen_Regenautomatik_Grenzwert
                          title: Grenzwert Regen (mm)
                          icon: f7:chart_bar
                          color: blue
                          raised: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: false
                          min: 0
                          max: 90

Hope it helps.

4 Likes

i leave it to Stefan to further comment on this widget, but thank you so much @RGroll for sharing your knowledge. I believe this widget with the expandable opportunities gives quite some possiblities to build functionable widgets, eg. for thermostate control and some other stuff and to replace the popup / popover hickups.

Thanks a lot Rainer - much appreciated :slight_smile:
Without your help I would have never find this card-expandable-animate-width - even I read the Card doc already :frowning:

FYI - I had to change the width of the f7-card from 100% to auto.
Otherwise the Widgets was right aligned on the mobile screen.
But now it is perfect :slight_smile:
I will review the code again - I already found a WhnZ_Licht_Main_Dimm item, which doesn’t belong to my watering system :smiley:

Many thanks again!

1 Like

Good to know

Ups, a relict from the quick testing - will update the code in case someone want to use it :smiley:

Happy to help!

once again @stefan13 thanks for sharing this really very useful and nice widget…

4 Likes

Hallo Stefan,
sehr schönes Widget. Ich möchte sowas ähnliches für schaltbare Steckdosen machen. Wie hast du denn den Zeittrigger gemacht?
Ich habe das bisher mir einem Rule gemacht:


rule "rule-pumpe"
when
//	System started
//or
	Time cron "0 0 */12 * * ?"  // Alle 12 Stunden
	//Time cron "0 0 1 1 * ?" //once per year
then

Sowie ich es sehe ist Bewaesserung_Rasen_Timer_Startzeit vom Typ DateTime.
Vergleichst du in einem Rule periodisch Bewaesserung_Rasen_Timer_Startzeit ?

Gruss
Marco

English please !!!

Ups sorry.

Hi Stefan,
very nice widget. I’d like to do something similar with switchable plug socket.
How did you do the time trigger?
In my rules i used a cron job like thus:

rule "rule-pumpe"
when
//	System started
//or
	Time cron "0 0 */12 * * ?"  // Alle 12 Stunden
	//Time cron "0 0 1 1 * ?" //once per year
then

As I see from your widget Bewaesserung_Rasen_Timer_Startzeit is of Typ DateTime.
Are you comparing in a rule Bewaesserung_Rasen_Timer_Startzeit with the current time?

Thanks
Marco

Hi Marco,

I do indeed the comparison like this…every minute…this is not the best way in terms of system performance and there might be better ways to do it, but it works well in my case. My openHab runs in a docker on a Synology so plenty of performance.

rule "Start Scheduled - Rasen"
when
	  Time cron "0 * * * * ? *"			
then 

 val startdate = (Zeit_Bewaesserung.state as DateTimeType).getZonedDateTime()

 if (now.isBefore(startdate)) { return; } 
 
 if (now.isAfter(startdate)) 		
 
	{..... do some stuff....

If you are using the most recent milestone, you don’t need the cron anymore. You can use the state of an DateTime item as a time trigger:
image

1 Like

Hi Jan,
I did this similar but on smaller devices (e.g RasPi 3) this could generate quite some load especially if you have a couple of these triggers.
Thanks for clarifying
Marco

Hi Justing,
this is interesting. Is it possible to have that trigger also in a DSL rule or do I have to use the GUI.
Currently I am on 3.3.0 Release Built but for this I would upgrade.
Thanks
Marco

I haven’t tried it, but I believe it should work with in DSL as well. I think it’s supposed to be something like:

when
  Time is ItemName

That rule only triggers at the item’s time on the item’s date. If you want the rule to trigger only on the time everyday (that is, ignore the date) then you add timeOnly to it:

when
  Time is ItemName timeOnly
1 Like

Hi Justin,
thank you for the hint and the example.
This works in 3.4.0.M3
With this new time based features can be added.
The goal is to have a widget were the on and off times of a switch item can be configured.
For now I will use the Time Picker from here:

But maybe there are other options available in OH3 now.

This is certainly one perfectly reasonable way to go. There are a couple other widgets in the market place that do something similar, if I recall so have a look around. I know rlkoshak has put a simple DateTime widget in the marketplace. If you want something more involved than that, there’s the fancy timeline widget.

If you have something very particular in mind though, your best chance of getting exactly what you want it to make it yourself. Look at the code of these simple widgets and see how they work to change an item’s time and then see if you can build the exact widget you are dreaming of.

I have a much more basic question regarding expandable f7-cards.
How do I control the height of the expanded card? I have this card which always opens full-screen which especially on the phone doesn’t make sense. Here when it is expanded:

I would like to have it opened only in the centre of the screen and with a height to ‘end’ below the orange and the cyan labels.

Here is the code:

uid: widget_PV_expandable
tags: []
props:
  parameters: []
  parameterGroups: []
timestamp: Feb 19, 2023, 9:00:41 PM
component: f7-card
config:
  backdrop: true
  class:
    - card-expandable-animate-width
  expandable: true
  style:
    height: 100px
    width: auto
  swipeToClose: true
slots:
  default:
    - component: oh-button
      config:
        class:
          - cell-open-button
          - card-opened-fade-out
        color: black
        iconMaterial: solar_power
        iconSize: 30px
        style:
          padding-bottom: 35px
          padding-top: 10px
          position: absolute
          right: 0
          top: 0
          z-index: 999
    - component: f7-card-content
      config:
        style:
          width: 100%
      slots:
        default:
          - component: oh-button
            config:
              class:
                - card-opened-fade-in
                - cell-close-button
                - card-close
              color: black
              iconF7: xmark_circle_fill
              iconSize: 30px
              style:
                padding-bottom: 35px
                padding-top: 10px
                position: absolute
                right: 0
                top: 0
                z-index: 999
          - component: f7-block
            config:
              class:
                - no-padding
              style:
                margin: 0px
                height: 220px
            slots:
              default:
                - component: f7-row
                  config:
                    style:
                      flex-wrap: nowrap
                      height: 50px
                      white-space: nowrap
                  slots:
                    default:
                      - component: f7-col
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  font-size: 20px
                                  font-weight: 600
                                  overflow: hidden
                                  text-overflow: ellipsis
                                  white-space: nowrap
                                text: PV-Status
                - component: f7-row
                  config:
                    class:
                      - justify-content-flex-start
                    style:
                      flex-wrap: nowrap
                      height: 30px
                      white-space: nowrap
                      font-size: 14px
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: bolt
                          size: 15
                          style:
                            margin-right: 5px
                      - component: Label
                        config:
                          style:
                            margin-right: 15px
                          text: =items.PV_Current_Consumption.displayState.replace("-", "")
                      - component: f7-icon
                        config:
                          f7: sun_max
                          size: 15
                          style:
                            margin-right: 5px
                      - component: Label
                        config:
                          style:
                            margin-right: 5px
                            color: "=(items.PV_Current_Feed.state > 0) ? 'red' : 'green'"
                          text: =items.PV_Current_Power.displayState
                      - component: f7-icon
                        config:
                          f7: money_euro
                          size: 15
                          style:
                            margin-right: 5px
                      - component: Label
                        config:
                          style:
                            margin-right: 5px
                          text: =items.aWATTar_CurrentPrice.displayState
          - component: f7-block
            config:
              class:
                - card-prevent-open
                - card-content-padding
              style:
                height: 50px
            slots:
              default:
                - component: f7-row
                  slots:
                    default:
                      - component: oh-trend
                        config:
                          trendItem: PV_Current_Feed
                          style:
                            background: "#DEDEDE"
                            trendGradient: "#000000"
                            position: absolute
                            width: 80%
                            height: 100
                            top: -120
                            left: 70
                - component: f7-row
                  slots:
                    default:
                      - component: f7-col
                        config:
                          style:
                            border-radius: 10px
                            padding: 5px
                            background: "=(items.PV_Current_Feed.state > 0) ? '#FF9B5D' : '#DCFFBE'"
                            color: "=(items.PV_Current_Feed.state > 0) ? '#DEDEDE' : '#000000'"
                        slots:
                          default:
                            - component: Label
                              config:
                                text: "=(items.PV_Current_Feed.state > 0) ? 'Aktueller Verbrauch' : 'Aktuelle Einspeisung'"
                            - component: Label
                              config:
                                margin-top: 0px
                                text: =items.PV_Current_Feed.displayState.replace("-", "")
                      - component: f7-col
                        config:
                          style:
                            border-radius: 10px
                            padding: 5px
                            background: "#B7FFFA"
                            color: "#2968D7"
                        slots:
                          default:
                            - component: Label
                              config:
                                text: "='niedrigster Tarif heute: ' + items.aWATTar_BestPrice_price.state"
                            - component: Label
                              config:
                                text: "='von: ' + items.aWATTar_BestPrice_start.displayState + ' - ' + items.aWATTar_BestPrice_end.displayState"

The short answer is “not easily”. The change in card size when expanded is accomplished under-the-hood by the f7 library with calculations of what the final size should be and the animations required to make that smooth. You can override the final height using a stylesheet to apply custom css to the opened card, but unless you want the animation to still expand to a full screen and then suddenly shrink back down to your set height at the end, you’d also have to identify all the animation classes and override all the animation settings and recalculate those for every different screen size.

On larger screens, you have access to 2 f7 css variables, --f7-card-expandable-tablet-height and --f7-card-expandable-tablet-width which allow you set the size of the card when it is determined by the library that it should be smaller than full screen, but on phone screens these have no effect.