OH3 Widget f7-badge "flash" color?

Hi All
I am trying to replicate an alarm keypad (as close as possible) within a Custom Widget.

One of the indicator lights should flash when the panel is in “Exit Delay”, so the three status conditions would be:
ON: - red
OFF: - gray
FLASHING: - flashing between red and gray

My code so far looks as follows:

    - component: f7-badge
      config:
        bgColor: '=(items.Panel_ACPowerOn.state === "ON") ? "red" : "gray"'
        style:
          border-radius: 20px
          height: 20px
          left: 10px
          position: absolute
          top: 240px
          width: 25px

I am however not able to find a way to implement the FLASHING condition.

Does anyone have a solution/suggestion that I can try?

Thanks Mark

Basically you’ve got two options of you want things changing in a widget: tie the graphic to a changing item state or css animation.

Creating an item that drives the flashing would be somewhat involved.

Css animation is reasonably easy to incorporate these days using stylesheet

Here’s one example, there are a few others around:

Thanks Justin. As usual you pointed me in the right direction. Once I used the correct terminology I was able to find an example to use to get the animation part to flash the colours and your linked example helped me construct a working basic widget as follows:

uid: Test_Colour_Animation
tags: []
props:
  parameters:
    - description: A text prop
      label: Prop 1
      name: prop1
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Dec 22, 2022, 5:39:15 PM
component: f7-page
config:
  stylesheet: >
    .myDIV {
      animation: mymove 1s infinite;
    } @keyframes mymove {
      from {background-color: orange;}
      to {background-color: gray;}
    }
slots:
  default:
    - component: f7-badge
      config:
        class: myDIV
        style:
          border-radius: 20px
          height: 20px
          left: 10px
          position: absolute
          top: 240px
          width: 25px

Giving:

Flasher

Please can you point me in a direction now to be able to base the colour on an expression.

So something like:

bgColor: '=(items.ZonesClosed.state === "ON") ? "orange" : items.PartitionExitDelay.state === "ON") ? *ANIMATION* : "gray"'

I have no idea where do this?

The only other options I can think of is to use 3 x f7-badge with visibility to decide which one is active - just seems too complex?

Thanks

You cannot use expressions in the stylesheet. What you have to do is put a style object in the same config and set a css variable in that object, which you can do with an expression.

config:
  style:
    --blink-color1: red
    --blink-color2: yellow

And then you can reference that variable in the stylesheet

  stylesheet: >
    .myDIV {
      animation: mymove 1s infinite;
    } @keyframes mymove {
      from {background-color: var(--blink-color1);}
      to {background-color: var(--blink-color2);}
    } 

Thanks Justin. Maybe I was not clear on what I needed, but you have helped me solve it anyway.

My working code:

uid: Test_Colour_Animation
tags: []
props:
  parameters:
    - description: A text prop
      label: Prop 1
      name: prop1
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Dec 22, 2022, 5:39:15 PM
component: f7-page
config:
  stylesheet: >
    .paradoxFlash {
      animation: mymove 0.5s infinite;
    } @keyframes mymove {
      from {background-color: green;}
      to {background-color: orange;}
    }
    .paradoxOrange {
      background: orange
    }    
    .paradoxGray {
      background: gray
    }    
slots:
  default:
    - component: f7-badge
      config:
        class: '=(items.ParaTest1.state === "ON") ? "paradoxFlash" : (items.ParaTest2.state === "ON") ? "paradoxOrange" : "paradoxGray"'
        style:
          border-radius: 20px
          height: 20px
          left: 10px
          position: absolute
          top: 240px
          width: 25px

So when Paratest1 is ON (regardless of Paratest2), get paradoxFlash, when Paratest2 in ON (only if Paratest1 is OFF), get paradoxOrange and when neither ON, get paradoxGray.

Thanks once again.

Is it correct that you cannot use this for background of the widget itself?

component: f7-card
config:
  stylesheet: >
          .background {
               animation: flashingbg 4s linear 0s infinite;
               color: red;
           }
           @keyframes flashingbg {
               0%   {background-color: #fff;}
               25%  {background-color: #04d4ce;}
               50%  {background-color: #04d4ce;}
               75%  {background-color: #fff;}
               100% {background-color: #fff;}
           }
  class: background
  style:
    border-radius: 15px
    box-shadow: 0px 0px
    margin: 5px 5px

Is not working here. When I put the class: background in a oh-link component in the widget however, it does work.

Yes and no. I’ve never looked into the details of how the stylesheet is implemented, but there does appear to be some matter of timing. If you were to change the selector in your sheet from .background to div, I think you would find that it works. But it seems that the f7 classes, and any classes you apply manually are added to the element after the stylesheet is processed, so selectors based on class (or id for that matter) won’t work on the same level as the stylesheet.

The easy solution for you here is to make an f7-block the base component of the element and put the stylesheet at that level and then make the card a child of that block.

Thank you for the tip Justin.

I managed to got it working with your first suggestion (using div selector) but failed to get it working with the second tip. This latter solution also creates a lot of other problems for me and the layout.

Now the next problem I have is that this stylesheet property in the widget does not get parsed (at least it seems so) and therefore I cannot create an animation based on an object status.

What I want is a background color ‘off’ (white) when my alarm status is 0. A color ‘on’ (green) when my alarm status is 2 and flashing white/green when it has status 1.

My final workaround is some SVG (1 ‘pixel’) from which one has animation and then use the background property.

You are correct.

See the post above for how to make the stylesheet dynamic:

It sounds like it might just make more sense to do this is classes. Those are easily added to components with expressions. So create the flashing animation with a particular class name in the stylesheet at the very top level of the widget and then just apply the class if the given condition is met.

Hi Justin,

Sorry for my late replies (kind of busy) but I cannot get it to work with your f7-block suggestion. This is what you mean right?

uid: AlarmStatusSmall
tags: []
props:
  parameters:
    - description: Header icon
      label: Icon
      name: iconHeader
      required: false
      type: TEXT
      groupName: header
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
      groupName: header
    - context: item
      description: Item to control
      label: Item
      name: item
      required: false
      type: TEXT
      groupName: setup
  parameterGroups:
    - name: header
      description: Widget general settings
    - name: setup
      description: Widget specific settings
timestamp: Feb 13, 2023, 10:24:01 AM
component: f7-block
config:
  stylesheet: >
    .background
    {
      background-color: red;
    }
slots:
  content:
    component: f7-card
    config:
      class:
        background
    slots:
      content:
        - component: f7-row
          config:
            style:
              justify-content: center
          slots:
            default:
              - component: oh-image
                config:
                  url: ='/static/icons/' + props.iconHeader + '.svg'
                  style:
                    height: 20px
                  visible: "=props.iconHeader ? true : false"

You’ve got a few errors in the yaml that are preventing this from working.

  1. the f7-block doesn’t have content slot. You should just use default instead.

  2. when you indented the f7-card you didn’t change it’s definition. Only the base component of the widget has that pattern of configuration:

component: base-component
config:
  config options...

any of the child elements must have the other (component array) setup:

- component: all-child-components
  config:
    config options...
1 Like

Thanks again for your help and patience. I got it working! Here is the code for anyone finding this topic some day:

uid: AlarmStatusSmall
tags: []
props:
  parameters:
    - description: Header icon
      label: Icon
      name: iconHeader
      required: false
      type: TEXT
      groupName: header
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
      groupName: header
    - context: item
      description: Item to control
      label: Item
      name: item
      required: false
      type: TEXT
      groupName: setup
  parameterGroups:
    - name: header
      description: Widget general settings
    - name: setup
      description: Widget specific settings
timestamp: Feb 26, 2023, 11:30:16 PM
component: f7-block
config:
  style:
    margin: 0px
    padding: 0px
  stylesheet: >
    .partial {
      animation: flashingbg 4s linear 0s infinite;
      color: red;
    }

    .on {
      background-color: #4ad194;
    }

    .off {
      background-color: white;
    }

    @keyframes flashingbg {
      0%   {background-color: #fff;}
      25%  {background-color: #4ad194;}
      50%  {background-color: #4ad194;}
      75%  {background-color: #fff;}
      100% {background-color: #fff;}
    }
slots:
  default:
    - component: f7-card
      config:
        class: "=items[props.item].state == 1 ? 'partial' : (items[props.item].state == 0) ? 'off' : 'on'"
        style:
          border-radius: 15px
          box-shadow: 0px 0px
          margin: 5px 5px
      slots:
        content:
          - component: f7-row
            config:
              style:
                justify-content: center
            slots:
              default:
                - component: oh-image
                  config:
                    url: ='/static/icons/' + props.iconHeader + '.svg'
                    style:
                      height: 20px
                    visible: "=props.iconHeader ? true : false"

1 Like