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.
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
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.
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
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).
Give a few days to appreciate everything you documented and to try it out!
This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.