Multi Sensor (Fibaro/Aeon/Philio/Aeotec)

Here is my first effort at a widget for OH3. It was designed to support the Fibaro motion sensor that provides 5 items, but could easily be adapted to any application where a thing provides a number of items to display.

Design objectives

  • Display a number of items. If possible, be an extensible design to support as many items as needed for other devices.
  • Function and look good on laptop and mobile screens. Be touch compliant on small screens.
  • Be flexible to allow the display of all or some of the items supported by the device.
  • Support easy format customisation (Iā€™m not sure on my overall theme design as yet).
  • Conditionally highlight icons that require user attention e.g. when the battery level matches or is below a specified threshold.
  • Allow drill down to the default widget / graph associated with each item.

I took inspiration from with work on the UI Widget: Weather, specifically the card widget by @RGroll and @oh11. In particular their use of the Swiper component provided a good solution the screen size/resolution issues. Their solution was a great way to start learning the new UI.

Here is the result:


Large screen showing all items being displayed.

Fibaro_small
Small screen showing swiper functionality to access icons that donā€™t fit on screen.

So the final feature set / some useful stuff I learnt:

  • Most of the features listed in the objectives above (maybe a better solution for extensibility).
  • Support for background / font / icon colour selection based on theme and/or any of the HTML colour standards (RGB/RGBA/HEX/HSL). Note you must specify the full format rgba(0,0,0,0) etc.
  • How to add an external link within the parameter description to access relevant reference materials.
  • How to drill down to display analyser data for each item.

Things that could be better/improved:

  • In portrait mode on small screens swiping arrows overlap the buttons, requiring careful clicking (or the analyzer graphs are displayed for the icon. Not so easy with my fat fingers :stuck_out_tongue_winking_eye: Need to experiment with the swiper padding options.
  • Avoided a repeater as data types (number / text), warning conditions and icons are different for each item. May be worth looking into now I have learnt more :wink:

Resources

Here is the YAML:

uid: fibaro_motion_card
tags: []
props:
  parameters:
    - context: item
      description: Name of the item containing motion sensor data. If undefined will not display the associated icon/text.
      label: Item Name (Motion sensor)
      name: item1
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing temperature sensor data. If undefined will not display the associated icon/text.
      label: Item Name (Temperature sensor)
      name: item2
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing light sensor / luminescence data. If undefined will not display the associated icon/text.
      label: Item name (Light sensor)
      name: item3
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing accelerometer / tamper alarm data. If undefined will not display the associated icon/text.
      label: Item name (Accelerometer sensor / Tamper alarm)
      name: item4
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing battery charge level data. If undefined will not display the associated icon/text.
      label: Item name (Battery level)
      name: item5
      required: false
      type: TEXT
      groupName: items
    - description: Highlight the motion sensor icon/text when the value equals the specified warning value e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Motion sensor)
      name: warn1
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the temperature icon/text when the value is LESS than the specified warning value e.g. 10. When undefined, no highlighting is displayed.
      label: Warning (Temperature sensor)
      name: warn2
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the light sensor icon/text when the value is LESS that the specified warning value e.g. 5. When undefined, no highlighting is displayed.
      label: Warning (Light sensor)
      name: warn3
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the tamper alarm icon/text when the value equals the specified warning value item e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Accelerometer sensor / Tamper alarm)
      name: warn4
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the battery level icon/text when the value is LESS than the specified warning level e.g. 15. When undefined, no highlighting is displayed.
      label: Warning (Battery level)
      name: warn5
      required: false
      type: TEXT
      groupName: warn
    - description: Set card header title. Only displays if populated.
      label: Card Title
      name: title
      required: false
      type: TEXT
      groupName: general
    - description: Background image URL e.g. http://10.1.0.1/static/fibaromotion.png
      label: Background image URL
      name: backgroundUrl
      required: false
      type: TEXT
      groupName: general
    - description: Intensity of the background-blur (0 - 10)
      label: Background image blur
      name: backgroundBlur
      required: false
      type: TEXT
      groupName: general
    - context: color
      description: Specify the background color for card. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-white. <b>Default = --f7-color-white</b>
      label: Background color
      name: backgroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for text and icons. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-black. <b>Default = --f7-color-black</b>
      label: Foreground color (normal)
      name: foregroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for highlighted text / icons (when warning condition is true). Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-red. <b>Default = --f7-theme-color</b>
      label: Foreground color (warning)
      name: foregroundColorWarn
      required: false
      type: TEXT
      groupName: general
  parameterGroups:
    - name: general
      label: Display options
    - name: items
      label: Items
    - name: warn
      label: Warning settings
timestamp: Jan 17, 2021, 2:45:45 AM
component: f7-card
config:
  title: =props.title
  class:
    - padding
  style:
    background-image: ='url(' + props.backgroundUrl + ')'
    backdrop-filter: ='blur(' + props.backgroundBlur + 'px)'
    background-size: contain
    background-repeat: no-repeat
    background-position: 100% 100%
    background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"
    border-radius: 20px
    overflow: hidden
    -ms-user-select: none
    -moz-user-select: none
    -webkit-user-select: none
    user-select: none
    --normal-txt-color: "=(props.foregroundColor === undefined ? 'var(--f7-color-black)' : (props.foregroundColor.substring(0,2) === '--' ? 'var(' + props.foregroundColor + ')' : props.foregroundColor))"
    --warn-txt-color: "=(props.foregroundColorWarn === undefined ? 'var(--f7-theme-color)' : (props.foregroundColorWarn.substring(0,2) === '--' ? 'var(' + props.foregroundColorWarn + ')' : props.foregroundColorWarn))"
slots:
  default:
    - component: f7-block
      config:
        class:
          - no-padding
          - no-margin
        style:
          width: 100%
          height: 100%
          position: absolute
          top: 0
          left: 0
          border-radius: 20px
    - component: f7-swiper
      config:
        navigation: true
        class:
          - padding-top
        params:
          initalSlide: 0
          runCallbacksOnInit: true
          grabCursor: true
          observer: true
          observeSlideChildren: true
          updateOnWindowResize: true
          spaceBetween: 5
          mousewheel: true
          keyboard: true
          watchOverflow: true
          breakpoints:
            "0":
              slidesPerView: 1
            "240":
              slidesPerView: 2
            "320":
              slidesPerView: 3
            "480":
              slidesPerView: 4
            "640":
              slidesPerView: 5
        style:
          --swiper-navigation-size: 30px
          --swiper-navigation-color: var(--normal-txt-color)
      slots:
        default:
          - component: f7-swiper-slide
            config:
              id: 1
              expandable: true
              visible: =!(props.item1 === undefined)
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: =[props.item1]
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: radiowaves_right
                          size: 40
                          style:
                            padding-right: 5px
                            color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
                      - component: Label
                        config:
                          text: =items[props.item1].state
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
          - component: f7-swiper-slide
            config:
              id: 2
              expandable: true
              visible: =!(props.item2 === undefined)
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: =[props.item2]
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: thermometer
                          size: 40
                          style:
                            padding-right: 5px
                            color: '=(items[props.item2].state < props.warn2 ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
                      - component: Label
                        config:
                          text: =items[(props.item2)].state
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: '=(items[props.item2].state < props.warn2 ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
          - component: f7-swiper-slide
            config:
              id: 3
              expandable: true
              visible: =!(props.item3 === undefined)
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: =[props.item3]
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: sun_max_fill
                          size: 40
                          style:
                            padding-right: 5px
                            color: '=(Number(items[props.item3].state) < Number(props.warn3) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
                      - component: Label
                        config:
                          text: =items[(props.item3)].state + " Lux"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: '=(Number(items[props.item3].state) < Number(props.warn3) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
          - component: f7-swiper-slide
            config:
              id: 4
              expandable: true
              visible: =!(props.item4 === undefined)
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: =[props.item4]
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: graph_square_fill
                          size: 40
                          style:
                            padding-right: 5px
                            color: '=(items[props.item4].state === props.warn4) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
                      - component: Label
                        config:
                          text: =items[(props.item4)].state
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: '=(items[props.item4].state === props.warn4) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
          - component: f7-swiper-slide
            config:
              id: 5
              expandable: true
              visible: =!(props.item5 === undefined)
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: =[props.item5]
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          material: battery_charging_full
                          size: 40
                          style:
                            padding-right: 5px
                            color: '=(Number(items[props.item5].state) < Number(props.warn5) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
                      - component: Label
                        config:
                          text: =items[(props.item5)].state + " %"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: '=(Number(items[props.item5].state) < Number(props.warn5) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'

Let me know if there are any ways in which this code could be improved. Always happy to learn. Iā€™d also like to thank @ysc for the great work on the new UI.

Andy

7 Likes

Version 2, now supports any multi-sensor device with up to 7 different items/outputs. If the item is not populated in the widget properties, it is hidden from view. As standard:

  1. Motion sensor
  2. Door contact
  3. Temperature
  4. Light Intensity / UV
  5. Humidity
  6. Tamper
  7. Battery

Duplicate code has been removed and replaced with an oh-repeater:

          - component: oh-repeater
            config:
              sourceType: unset
              for: i
              in:
                - - item1
                  - warn1
                  - f7
                  - radiowaves_right
                  - ==
                - - item2
                  - warn2
                  - material
                  - sensor_door
                  - ==
                - - item3
                  - warn3
                  - f7
                  - thermometer
                  - <
                - - item4
                  - warn4
                  - f7
                  - sun_max_fill
                  - <
                  - Lux
                - - item5
                  - warn5
                  - f7
                  - drop_fill
                  - ">"
                - - item6
                  - warn6
                  - f7
                  - graph_square_fill
                  - ==
                - - item7
                  - warn7
                  - material
                  - battery_charging_full
                  - <

Each item in the ā€œarrayā€ represents the variables for each button/slide:

                - - item1
                  - warn1
                  - f7
                  - radiowaves_right
                  - ==
  1. item1 is the property name that contains the item
  2. warn1 is the warning value property for the item
  3. ā€™f7 is the icon library required (possible values f7 or material)
  4. radiowaves_right is the icon name from either f7 or material icon list (openHAB icons not supported)
  5. == operator used for warning highlight/colour to be applied e.g. garage temperature in the image above. Valid values are == highlight when item equals warning value, < item less than warning value, > item greater than warning value.

Icons can be customised by changing sections 3/4. Warning highlight operator can be changed by amending 5 (you may also want to amend the associated warning property description to avoid confusion).

Syntax for referencing items in the unset for/loop took me ages to work out. Hopefully this may help someone else save some time:

'=items[props[loop.i[0]]].state'

Also improved the layout so that the navigation arrows donā€™t overlap with the slider buttons.

Changed the UID to reflect the more generic nature of the widget.

Here is the code:

uid: multisensor_card
tags: []
props:
  parameters:
    - context: item
      description: Name of the item containing motion sensor data. If undefined will not display the associated icon/text.
      label: Item Name (Motion sensor)
      name: item1
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing door contact data. If undefined will not display the associated icon/text.
      label: Item Name (Door Contact)
      name: item2
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing temperature sensor data. If undefined will not display the associated icon/text.
      label: Item Name (Temperature sensor)
      name: item3
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing light sensor / luminescence data. If undefined will not display the associated icon/text.
      label: Item name (Light sensor)
      name: item4
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing humidity data. If undefined will not display the associated icon/text.
      label: Item name (Humidity)
      name: item5
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing accelerometer / tamper alarm data. If undefined will not display the associated icon/text.
      label: Item name (Accelerometer sensor / Tamper alarm)
      name: item6
      required: false
      type: TEXT
      groupName: items
    - context: item
      description: Name of the item containing battery charge level data. If undefined will not display the associated icon/text.
      label: Item name (Battery level)
      name: item7
      required: false
      type: TEXT
      groupName: items
    - description: Highlight the motion sensor icon/text when the value equals the specified warning value e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Motion sensor)
      name: warn1
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the door contact icon/text when the value equals the specified warning value e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Door contact)
      name: warn2
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the temperature icon/text when the value is LESS than the specified warning value e.g. 10. When undefined, no highlighting is displayed.
      label: Warning (Temperature sensor)
      name: warn3
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the light sensor icon/text when the value is LESS that the specified warning value e.g. 5. When undefined, no highlighting is displayed.
      label: Warning (Light sensor)
      name: warn4
      required: false
      type: INTEGER
      groupName: warn
    - description: Highlight the humidity sensor icon/text when the value is GREATER that the specified warning value e.g. 5. When undefined, no highlighting is displayed.
      label: Warning (Humidity sensor)
      name: warn5
      required: false
      type: INTEGER
      groupName: warn
    - description: Highlight the tamper alarm icon/text when the value equals the specified warning value item e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Accelerometer sensor / Tamper alarm)
      name: warn6
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the battery level icon/text when the value is LESS than the specified warning level e.g. 15. When undefined, no highlighting is displayed.
      label: Warning (Battery level)
      name: warn7
      required: false
      type: INTEGER
      groupName: warn
    - description: Set card header title. Only displays if populated.
      label: Card Title
      name: title
      required: false
      type: TEXT
      groupName: general
    - description: Background image URL e.g. http://10.1.0.1/static/fibaromotion.png
      label: Background image URL
      name: backgroundUrl
      required: false
      type: TEXT
      groupName: general
    - description: Intensity of the background-blur (0 - 10)
      label: Background image blur
      name: backgroundBlur
      required: false
      type: TEXT
      groupName: general
    - context: color
      description: Specify the background color for card. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-white. <b>Default = --f7-color-white</b>
      label: Background color
      name: backgroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for text and icons. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-black. <b>Default = --f7-color-black</b>
      label: Foreground color (normal)
      name: foregroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for highlighted text / icons (when warning condition is true). Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-red. <b>Default = --f7-theme-color</b>
      label: Foreground color (warning)
      name: foregroundColorWarn
      required: false
      type: TEXT
      groupName: general
  parameterGroups:
    - name: general
      label: Display options
    - name: items
      label: Items
    - name: warn
      label: Warning settings
timestamp: Jan 23, 2021, 9:33:04 PM
component: f7-card
config:
  title: =props.title
  class:
    - padding
  style:
    background-image: ='url(' + props.backgroundUrl + ')'
    backdrop-filter: ='blur(' + props.backgroundBlur + 'px)'
    background-size: contain
    background-repeat: no-repeat
    background-position: 100% 100%
    background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"
    border-radius: 5px
    overflow: hidden
    -ms-user-select: none
    -moz-user-select: none
    -webkit-user-select: none
    user-select: none
    --normal-txt-color: "=(props.foregroundColor === undefined ? 'var(--f7-color-black)' : (props.foregroundColor.substring(0,2) === '--' ? 'var(' + props.foregroundColor + ')' : props.foregroundColor))"
    --warn-txt-color: "=(props.foregroundColorWarn === undefined ? 'var(--f7-theme-color)' : (props.foregroundColorWarn.substring(0,2) === '--' ? 'var(' + props.foregroundColorWarn + ')' : props.foregroundColorWarn))"
slots:
  default:
    - component: f7-swiper
      config:
        navigation: true
        class:
          - padding-top
        params:
          initalSlide: 0
          runCallbacksOnInit: true
          grabCursor: true
          observer: true
          observeSlideChildren: true
          updateOnWindowResize: true
          mousewheel: true
          keyboard: true
          watchOverflow: true
          slidesOffsetBefore: 30
          slidesOffsetAfter: 30
          breakpoints:
            "0":
              slidesPerView: 1
              spaceBetween: 0
            "240":
              slidesPerView: 2
              spaceBetween: 0
            "320":
              slidesPerView: 3
              spaceBetween: 0
            "480":
              slidesPerView: 4
              spaceBetween: 5
            "640":
              slidesPerView: auto
              spaceBetween: 5
        style:
          --swiper-navigation-size: 30px
          --swiper-navigation-color: var(--normal-txt-color)
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: unset
              for: i
              in:
                - - item1
                  - warn1
                  - f7
                  - radiowaves_right
                  - ==
                - - item2
                  - warn2
                  - material
                  - sensor_door
                  - ==
                - - item3
                  - warn3
                  - f7
                  - thermometer
                  - <
                - - item4
                  - warn4
                  - f7
                  - sun_max_fill
                  - <
                  - Lux
                - - item5
                  - warn5
                  - f7
                  - drop_fill
                  - ">"
                - - item6
                  - warn6
                  - f7
                  - graph_square_fill
                  - ==
                - - item7
                  - warn7
                  - material
                  - battery_charging_full
                  - <
              fragment: true
            slots:
              default:
                - component: f7-swiper-slide
                  config:
                    id: 1
                    expandable: true
                    visible: =!(props[loop.i[0]] === undefined)
                    style:
                      border-radius: 5px
                      width: 100px
                  slots:
                    default:
                      - component: oh-button
                        config:
                          action: analyzer
                          actionAnalyzerItems: =[props[loop.i[0]]]
                          class:
                            - justify-content-center
                            - align-items-start
                            - text-align-center
                          style:
                            height: 100%
                            --f7-button-hover-bg-color: var(--f7-color-white-shade)
                            --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                        slots:
                          default:
                            - component: f7-icon
                              config:
                                f7: '=(loop.i[2]==="f7" ? loop.i[3] : "")'
                                material: '=(loop.i[2]==="material" ? loop.i[3] : "")'
                                size: 40
                                style:
                                  padding-right: 5px
                                  color: '=(loop.i[4] == "==" ? (items[props[loop.i[0]]].state == props[loop.i[1]] ? "var(--warn-txt-color)" : "var(--normal-txt-color)") : (loop.i[4] == "<" ? (items[props[loop.i[0]]].state < props[loop.i[1]] ? "var(--warn-txt-color)" : "var(--normal-txt-color)") : "var(--normal-txt-color)"))'
                            - component: Label
                              config:
                                text: '=items[props[loop.i[0]]].state + (loop.i[5] === undefined ? "" : " " + loop.i[5])'
                                style:
                                  font-size: 18px
                                  font-weight: 400
                                  color: '=(loop.i[4] == "==" ? (items[props[loop.i[0]]].state == props[loop.i[1]] ? "var(--warn-txt-color)" : "var(--normal-txt-color)") : (loop.i[4] == "<" ? (items[props[loop.i[0]]].state < props[loop.i[1]] ? "var(--warn-txt-color)" : "var(--normal-txt-color)") : "var(--normal-txt-color)"))'

Let me know if there are any ways this can be improved. Andy

9 Likes

Very nice,

I will have to tweak my rip off version :blush:

I think one thing that might be worth a try is to make a version that assumes you have added equipment from the thing into the model including all of the points you want using default names. It would only be necessary then to set the equipment as all items would have names based on the equipment.

I think the challenge would be the zwave database would need to have consistent names for the equivalent endpoints for all supported devices so that it did not become very involved.

Based on your original (not latest) but only working for fibaro motion:

uid: fibaro_motion_equipment_card
tags: []
props:
  parameters:
    - context: item
      description: Name of the equipment containing motion sensor items. If undefined will not display the associated icon/text.
      label: Item Name (Motion sensor equipment)
      name: item1
      required: false
      type: TEXT
      groupName: items
    - description: Highlight the motion sensor icon/text when the value equals the specified warning value e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Motion sensor)
      name: warn1
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the temperature icon/text when the value is LESS than the specified warning value e.g. 10. When undefined, no highlighting is displayed.
      label: Warning (Temperature sensor)
      name: warn2
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the light sensor icon/text when the value is LESS that the specified warning value e.g. 5. When undefined, no highlighting is displayed.
      label: Warning (Light sensor)
      name: warn3
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the tamper alarm icon/text when the value equals the specified warning value item e.g. ON. When undefined, no highlighting is displayed.
      label: Warning (Accelerometer sensor / Tamper alarm)
      name: warn4
      required: false
      type: TEXT
      groupName: warn
    - description: Highlight the battery level icon/text when the value is LESS than the specified warning level e.g. 15. When undefined, no highlighting is displayed.
      label: Warning (Battery level)
      name: warn5
      required: false
      type: TEXT
      groupName: warn
    - description: Set card header title. Only displays if populated.
      label: Card Title
      name: title
      required: false
      type: TEXT
      groupName: general
    - description: Background image URL e.g. http://10.1.0.1/static/fibaromotion.png
      label: Background image URL
      name: backgroundUrl
      required: false
      type: TEXT
      groupName: general
    - description: Intensity of the background-blur (0 - 10)
      label: Background image blur
      name: backgroundBlur
      required: false
      type: TEXT
      groupName: general
    - context: color
      description: Specify the background color for card. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-white. <b>Default = --f7-color-white</b>
      label: Background color
      name: backgroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for text and icons. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-black. <b>Default = --f7-color-black</b>
      label: Foreground color (normal)
      name: foregroundColor
      required: false
      type: TEXT
      groupName: general
    - description: Specify the foreground color for highlighted text / icons (when warning condition is true). Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-red. <b>Default = --f7-theme-color</b>
      label: Foreground color (warning)
      name: foregroundColorWarn
      required: false
      type: TEXT
      groupName: general
  parameterGroups:
    - name: general
      label: Display options
    - name: items
      label: Items
    - name: warn
      label: Warning settings
timestamp: Jan 24, 2021, 1:39:40 PM
component: f7-card
config:
  title: =props.title
  class:
    - padding
  style:
    background-image: ='url(' + props.backgroundUrl + ')'
    backdrop-filter: ='blur(' + props.backgroundBlur + 'px)'
    background-size: contain
    background-repeat: no-repeat
    background-position: 100% 100%
    background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"
    border-radius: 20px
    overflow: hidden
    -ms-user-select: none
    -moz-user-select: none
    -webkit-user-select: none
    user-select: none
    --normal-txt-color: "=(props.foregroundColor === undefined ? 'var(--f7-color-black)' : (props.foregroundColor.substring(0,2) === '--' ? 'var(' + props.foregroundColor + ')' : props.foregroundColor))"
    --warn-txt-color: "=(props.foregroundColorWarn === undefined ? 'var(--f7-theme-color)' : (props.foregroundColorWarn.substring(0,2) === '--' ? 'var(' + props.foregroundColorWarn + ')' : props.foregroundColorWarn))"
slots:
  default:
    - component: f7-block
      config:
        class:
          - no-padding
          - no-margin
        style:
          width: 100%
          height: 100%
          position: absolute
          top: 0
          left: 0
          border-radius: 20px
    - component: f7-swiper
      config:
        navigation: true
        class:
          - padding-top
        params:
          initalSlide: 0
          runCallbacksOnInit: true
          grabCursor: true
          observer: true
          observeSlideChildren: true
          updateOnWindowResize: true
          spaceBetween: 5
          mousewheel: true
          keyboard: true
          watchOverflow: true
          breakpoints:
            "0":
              slidesPerView: 1
            "240":
              slidesPerView: 2
            "320":
              slidesPerView: 3
            "480":
              slidesPerView: 4
            "640":
              slidesPerView: 5
        style:
          --swiper-navigation-size: 30px
          --swiper-navigation-color: var(--normal-txt-color)
      slots:
        default:
          - component: f7-swiper-slide
            config:
              id: 1
              expandable: true
              visible: "=(items[((!props.item1) ? '' : props.item1) + '_MotionAlarm'].state != '-')"
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: "=[((!props.item1) ? '' : props.item1) + '_MotionAlarm']"
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: radiowaves_right
                          size: 40
                          style:
                            padding-right: 5px
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_MotionAlarm'].state === props.warn1) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
                      - component: Label
                        config:
                          text: "=items[((!props.item1) ? '' : props.item1) + '_MotionAlarm'].state"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_MotionAlarm'].state === props.warn1) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
          - component: f7-swiper-slide
            config:
              id: 2
              expandable: true
              visible: "=(items[((!props.item1) ? '' : props.item1) + '_Sensortemperature'].state != '-')"
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: "=[((!props.item1) ? '' : props.item1) + '_Sensortemperature']"
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: thermometer
                          size: 40
                          style:
                            padding-right: 5px
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_Sensortemperature'].state < props.warn2) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
                      - component: Label
                        config:
                          text: "=items[((!props.item1) ? '' : props.item1) + '_Sensortemperature'].state"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_Sensortemperature'].state < props.warn2) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
          - component: f7-swiper-slide
            config:
              id: 3
              expandable: true
              visible: "=(items[((!props.item1) ? '' : props.item1) + '_Sensorluminance'].state != '-')"
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: "=[((!props.item1) ? '' : props.item1) + '_Sensorluminance']"
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: sun_max_fill
                          size: 40
                          style:
                            padding-right: 5px
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_Sensorluminance'].state < props.warn3) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
                      - component: Label
                        config:
                          text: "=items[((!props.item1) ? '' : props.item1) + '_Sensorluminance'].state +' LUX'"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_Sensorluminance'].state < props.warn3) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
          - component: f7-swiper-slide
            config:
              id: 4
              expandable: true
              visible: "=(items[((!props.item1) ? '' : props.item1) + '_Sensorseismicintensity'].state != '-')"
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: "=[((!props.item1) ? '' : props.item1) + '_Sensorseismicintensity']"
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          f7: graph_square_fill
                          size: 40
                          style:
                            padding-right: 5px
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_Sensorseismicintensity'].state > props.warn4) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
                      - component: Label
                        config:
                          text: "=items[((!props.item1) ? '' : props.item1) + '_Sensorseismicintensity'].state"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: "=(items[((!props.item1) ? '' : props.item1) + '_Sensorseismicintensity'].state > props.warn4) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)'"
          - component: f7-swiper-slide
            config:
              id: 5
              expandable: true
              visible: "=(items[((!props.item1) ? '' : props.item1) + '_BatteryLevel'].state != '-')"
              style:
                border-radius: 5px
            slots:
              default:
                - component: oh-button
                  config:
                    action: analyzer
                    actionAnalyzerItems: "=[((!props.item1) ? '' : props.item1) + '_BatteryLevel']"
                    class:
                      - justify-content-center
                      - align-items-center
                      - text-align-center
                    style:
                      height: 100%
                      --f7-button-hover-bg-color: var(--f7-color-white-shade)
                      --f7-button-pressed-bg-color: var(--f7-color-white-shade)
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          material: battery_charging_full
                          size: 40
                          style:
                            padding-right: 5px
                            color: "=(Number(items[((!props.item1) ? '' : props.item1) + '_BatteryLevel'].state) < Number(props.warn5) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)')"
                      - component: Label
                        config:
                          text: "=items[((!props.item1) ? '' : props.item1) + '_BatteryLevel'].state + ' %'"
                          style:
                            font-size: 18px
                            font-weight: 400
                            color: "=(Number(items[((!props.item1) ? '' : props.item1) + '_BatteryLevel'].state) < Number(props.warn5) ? 'var(--warn-txt-color)' : 'var(--normal-txt-color)')"

Hi, I did consider what you described, but concluded that ā€˜consistent namesā€™ could be the achilles heel of such an approach. As is, it supports any multi-function devices or even a way to aggregate outputs from separate devices into a single UI panel.

Ok, there are a few more lines of code in the properties section and it takes a few more minutes to set up, but as this is usually a one off process, I thought the compromise made sense for the flexibility offered.

Glad someone has found this useful :grinning: !

Andy

This is a brilliant widget and has helped me understand some of the dark arts such as repeaters. I am trying to include this in a room based ā€œcardā€ and I cant for the life of me work out how to remove the outline or shadow - any idea?

Take a look at this this post

Although this talks about adding styles, the general tips on using F12/inspect should help. Add box-shadow: none to the style: section of the f7-card section:

component: f7-card
config:
  title: =props.title
  class:
    - padding
  style:
    ...
    background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"
    ...
    box-shadow: none

That should take away the shadow. I donā€™t think there is any border (looks transparent from what I can see). But you can use the inspect trick to look for the style.

If it still looks like there is a border, check the background colours. Not all things that look white are pure white :stuck_out_tongue_winking_eye: You can override the default background-color (ā€“f7-color-white) in the properties of the widget.