Dynamic light on floorplan

how to make a floorplan with the effect of turning off the lights in individual rooms? For example

Thanks for your answer, but this is not quite what I need

The feature you are looking for does not exist out of the box in any of the MainUI pages. There are some very advanced methods that you could use to get a similar behavior but you will be creating most of the framework from scratch.

If I were to try and do this myself, I would probably not even use the floorplan page, but a regular layout page with a base image underneath an svg where you can control the opacity and color of different paths that overlay a lighting effect on each room.

There is a very old post about doing something similar to what you seek in habpanel that also uses svgs, but again you are building most of it yourself:

Most of that should still work if you want to try and go that route.

1 Like

You mean something like that?


Note that the oh:glowingbulb is a custom icon I created myself which I am happy to share.

It is possible to use SVGs with mainui as well, if you are using more recent OH releases. By using trick @JustinG trick with one of other topics I was able to embed SVG and overlay it with semi dynamic content generated through OH mainui.

Below an example with SVG and just a text element placed on top of it. Obviously you can dive more and make it more appealing. In below case I’ve used widget, because text values are dynamic and can be switched between day/month/year.

    - component: svg
      config:
        xmlns: http://www.w3.org/2000/svg
        width: 100%
        height: 100%
        viewBox: 0 0 660 325
        style:
          filter: "=(themeOptions.dark === 'dark') ? 'invert()' : ''"
      slots:
        default:
          - component: use
            config:
              href: /static/hall-b.svg#hall-b
          - component: text
            config:
              x: 70
              y: 185
              fill: navy
              content: "... expression .."
              style:
                font-size: 1rem

1 Like

Does that relate to the floorplan or the fixed canvas layout page?

1 Like

Above is result of a backport of two commits from OH 3.3 or 3.4 to 3.0.x, so its a regular layout page. Widget gets placed as a single cell of a row. Its responsive, hence I didn’t look for fixed grid/canvas from later releases. Most relevant part is - you can draw svg circle with dynamic background depending on OH item state. :slight_smile:

1 Like

Ok, but what I wanted to know if it is a floor plan which it is not (but surely fine).

Can you post the complete widget. I did the following and nothing happened:

uid: widget_820d359ef5
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: Mar 1, 2024, 4:31:58 PM
component: svg
config:
  xmlns: http://www.w3.org/2000/svg
  width: 100%
  height: 100%
  viewBox: 0 0 660 325
  style:
    filter: "=(themeOptions.dark === 'dark') ? 'invert()' : ''"
slots:
  default:
    - component: use
      config:
        href: /static/glowingbulb-on.svg
    - component: text
      config:
        x: 70
        y: 185
        fill: navy
        content: ... expression ..
        style:
          font-size: 1rem

with the item-prop set to an item and the text-prop to “test”. I am sure the svg exists in the location but all I see is

image

The layout page is:

config:
  label: Hala B - rzut
  sidebar: true
  visibleTo:
    - role:administrator
    - role:user
  order: 53
blocks:
  - component: oh-block
    config: {}
    slots:
      default:
        - component: oh-grid-row
          config: {}
          slots:
            default:
              - component: oh-grid-col
                config: {}
                slots:
                  default:
                    - component: widget:floor_plan_hall_b
                      config: {}

Widget, without expressions is fairly basic:

uid: floor_plan_hall_b
tags: []
props:
  parameters: []
  parameterGroups: []
component: f7-card
config:
  style:
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.1)
    --f7-list-item-after-text-weight: 500
    --f7-list-item-after-text-color: var(--f7-list-item-title-font-color)
    --f7-list-item-after-font-size: 18px
    width: 100%
    height: 100%
slots:
  default:
    - component: f7-segmented
      config:
        round: true
        style:
          --f7-button-small-outline-border-width: 1px
        class:
          - padding-horizontal-half
      slots:
        default:
          - component: oh-button
            config:
              small: true
              outline: true
              actionVariable: period
              round: false
              action: variable
              actionVariableValue: _DAY
              text: Dzień
              fill: =!(vars.period !== undefined) || vars.period === '_DAY'
          - component: oh-button
            config:
              small: true
              outline: true
              actionVariable: period
              round: false
              action: variable
              actionVariableValue: _MONTH
              text: Miesiąc
              fill: =vars.period !== undefined && vars.period === '_MONTH'
          - component: oh-button
            config:
              small: true
              outline: true
              actionVariable: period
              round: false
              action: variable
              actionVariableValue: _YEAR
              text: Rok
              fill: =vars.period !== undefined && vars.period === '_YEAR'
    - component: svg
      config:
        xmlns: http://www.w3.org/2000/svg
        width: 100%
        height: 100%
        viewBox: 0 0 660 325
        style:
          filter: "=(themeOptions.dark === 'dark') ? 'invert()' : ''"
      slots:
        default:
          - component: use
            config:
              href: /static/hall-b.svg#hall-b
          - component: text
            config:
              x: 70
              y: 185
              fill: navy
              content: =items['Installations_Electricity_ElectricMeter_028_Total_Energy_Counter_Utilization_Yearly'].state
              style:
                font-size: 1rem
          - component: text
            config:
              x: 70
              y: 170
              fill: blue
              content: Sample 1
              style:
                font-size: 0.5rem
          - component: line
            config:
              x1: 45
              y1: 186
              x2: 146
              y2: 186
              stroke: blue
              stroke-width: 0.5
          - component: text
            config:
              x: 85
              y: 200
              fill: blue
              content: Sample 2
              style:
                font-size: 0.5rem
          - component: text
            config:
              x: 84
              y: 210
              fill: navy
              content: =items['Installations_Electricity_ElectricMeter_029_Total_Energy_Counter_Utilization_Yearly'].state
              style:
                font-size: 0.7rem
          - component: line
            config:
              x1: 45
              y1: 186
              x2: 80
              y2: 212
              stroke: blue
              stroke-width: 0.5
          - component: line
            config:
              x1: 80
              y1: 212
              x2: 146
              y2: 212
              stroke: blue
              stroke-width: 0.5
          - component: text
            config:
              x: 85
              y: 225
              fill: blue
              content: Sample 3
              style:
                font-size: 0.5rem
          - component: text
            config:
              x: 85
              y: 235
              fill: navy
              content: =items['Installations_Electricity_ElectricMeter_030_Total_Energy_Counter_Utilization_Yearly'].state
              style:
                font-size: 0.7rem
          - component: line
            config:
              x1: 45
              y1: 186
              x2: 80
              y2: 237
              stroke: blue
              stroke-width: 0.5
          - component: line
            config:
              x1: 80
              y1: 237
              x2: 146
              y2: 237
              stroke: blue
              stroke-width: 0.5
          - component: text
            config:
              x: 355
              y: 245
              fill: blue
              content: Sample 4
              style:
                font-size: 0.5rem
          - component: text
            config:
              x: 355
              y: 255
              fill: navy
              content: =items['Installations_Electricity_ElectricMeter_027_Total_Energy_Counter_Utilization_Yearly'].state
              style:
                font-size: 0.7rem
          - component: line
            config:
              x1: 365
              y1: 256
              x2: 365
              y2: 276
              stroke: blue
              stroke-width: 0.5
          - component: text
            config:
              x: 560
              y: 95
              fill: orange
              content: Sample 5
              style:
                font-size: 0.5rem
          - component: text
            config:
              x: 560
              y: 105
              fill: orange
              content: =items['Installations_Gas_GasMeter_001_Total_Energy_Counter_Utilization_Yearly'].state
              style:
                font-size: 0.7rem
          - component: line
            config:
              x1: 635
              y1: 70
              x2: 635
              y2: 106
              stroke: orange
              stroke-width: 0.5
          - component: line
            config:
              x1: 558
              y1: 106
              x2: 635
              y2: 106
              stroke: orange
              stroke-width: 0.5

Key aspect - the SVG have to have element with id=hall-b, otherwise it doesn’t work with href: /static/hall-b.svg#hall-b. Root of svg file I’ve used is:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   version="1.1"
   width="100%"
   viewBox="0 0 660 325"
   preserveAspectRatio="xMidYMid meet"
   id="hall-b"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
...
</svg>

The expressions I’ve replaced cause it is fairly long, I do generate them during system startup. It is used in conjunction with button bar, so its probably not that interesting. Yet it should work if you have a n item matching below naming scheme.

In one line it is:

content: "=( vars.period !== undefined && vars.period === '_YEAR' ? ( items['MyItem_Total_Energy_Counter_Utilization_Yearly'] !== undefined ? ( items['MyItem_Total_Energy_Counter_Utilization_Yearly'].displayState !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Yearly'].displayState.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Yearly'].displayState.trim() !== '-' ? items['MyItem_Total_Energy_Counter_Utilization_Yearly'].displayState : ( items['MyItem_Total_Energy_Counter_Utilization_Yearly'].state !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Yearly'].state.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Yearly'].state.trim() !== '-' ? ( items['MyItem_Total_Energy_Counter_Utilization_Yearly'].state === 'NULL' ? '  ' : ( items['MyItem_Total_Energy_Counter_Utilization_Yearly'].state === '❔' ? '  ' : items['MyItem_Total_Energy_Counter_Utilization_Yearly'].state ) ) : '' ) ) : '' ) : ( vars.period !== undefined && vars.period === '_MONTH' ? ( items['MyItem_Total_Energy_Counter_Utilization_Monthly'] !== undefined ? ( items['MyItem_Total_Energy_Counter_Utilization_Monthly'].displayState !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Monthly'].displayState.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Monthly'].displayState.trim() !== '-' ? items['MyItem_Total_Energy_Counter_Utilization_Monthly'].displayState : ( items['MyItem_Total_Energy_Counter_Utilization_Monthly'].state !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Monthly'].state.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Monthly'].state.trim() !== '-' ? ( items['MyItem_Total_Energy_Counter_Utilization_Monthly'].state === 'NULL' ? '  ' : ( items['MyItem_Total_Energy_Counter_Utilization_Monthly'].state === '❔' ? '  ' : items['MyItem_Total_Energy_Counter_Utilization_Monthly'].state ) ) : '' ) ) : '' ) : ( vars.period !== undefined && vars.period === '_QUARTER_HOUR' ? ( items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'] !== undefined ? ( items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].displayState !== undefined && items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].displayState.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].displayState.trim() !== '-' ? items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].displayState : ( items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].state !== undefined && items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].state.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].state.trim() !== '-' ? ( items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].state === 'NULL' ? '  ' : ( items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].state === '❔' ? '  ' : items['MyItem_Total_Energy_Counter_Utilization_QuarterHourly'].state ) ) : '' ) ) : '' ) : ( vars.period !== undefined && vars.period === '_MINUTE' ? ( items['MyItem_Total_Energy_Counter_Utilization_Minutely'] !== undefined ? ( items['MyItem_Total_Energy_Counter_Utilization_Minutely'].displayState !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Minutely'].displayState.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Minutely'].displayState.trim() !== '-' ? items['MyItem_Total_Energy_Counter_Utilization_Minutely'].displayState : ( items['MyItem_Total_Energy_Counter_Utilization_Minutely'].state !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Minutely'].state.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Minutely'].state.trim() !== '-' ? ( items['MyItem_Total_Energy_Counter_Utilization_Minutely'].state === 'NULL' ? '  ' : ( items['MyItem_Total_Energy_Counter_Utilization_Minutely'].state === '❔' ? '  ' : items['MyItem_Total_Energy_Counter_Utilization_Minutely'].state ) ) : '' ) ) : '' ) : ( vars.period !== undefined && vars.period === '_HOUR' ? ( items['MyItem_Total_Energy_Counter_Utilization_Hourly'] !== undefined ? ( items['MyItem_Total_Energy_Counter_Utilization_Hourly'].displayState !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Hourly'].displayState.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Hourly'].displayState.trim() !== '-' ? items['MyItem_Total_Energy_Counter_Utilization_Hourly'].displayState : ( items['MyItem_Total_Energy_Counter_Utilization_Hourly'].state !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Hourly'].state.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Hourly'].state.trim() !== '-' ? ( items['MyItem_Total_Energy_Counter_Utilization_Hourly'].state === 'NULL' ? '  ' : ( items['MyItem_Total_Energy_Counter_Utilization_Hourly'].state === '❔' ? '  ' : items['MyItem_Total_Energy_Counter_Utilization_Hourly'].state ) ) : '' ) ) : '' ) : ( !(vars.period !== undefined) || vars.period === '_DAY' ? ( items['MyItem_Total_Energy_Counter_Utilization_Daily'] !== undefined ? ( items['MyItem_Total_Energy_Counter_Utilization_Daily'].displayState !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Daily'].displayState.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Daily'].displayState.trim() !== '-' ? items['MyItem_Total_Energy_Counter_Utilization_Daily'].displayState : ( items['MyItem_Total_Energy_Counter_Utilization_Daily'].state !== undefined && items['MyItem_Total_Energy_Counter_Utilization_Daily'].state.trim().length > 0 && items['MyItem_Total_Energy_Counter_Utilization_Daily'].state.trim() !== '-' ? ( items['MyItem_Total_Energy_Counter_Utilization_Daily'].state === 'NULL' ? '  ' : ( items['MyItem_Total_Energy_Counter_Utilization_Daily'].state === '❔' ? '  ' : items['MyItem_Total_Energy_Counter_Utilization_Daily'].state ) ) : '' ) ) : '' ) : ( vars.period !== undefined && vars.period === '_Current' ? ( items['MyItem_Power'] !== undefined ? ( items['MyItem_Power'].displayState !== undefined && items['MyItem_Power'].displayState.trim().length > 0 && items['MyItem_Power'].displayState.trim() !== '-' ? items['MyItem_Power'].displayState : ( items['MyItem_Power'].state !== undefined && items['MyItem_Power'].state.trim().length > 0 && items['MyItem_Power'].state.trim() !== '-' ? ( items['MyItem_Power'].state === 'NULL' ? '  ' : ( items['MyItem_Power'].state === '❔' ? '  ' : items['MyItem_Power'].state ) ) : '' ) ) : '' ) : '' ) ) ) ) ) ) )"
              

Expression generator pre-dates @item.state syntax available in recent releases. Expression additionally handles situation when item is invisible to user (I sent then a special utf8 character through SSE).

2 Likes

Give a few days to appreciate everything you documented and to try it out! :star_struck:

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.