Floorplan Pages - SVG CSS / animation

Hi all,

I tried to use the floorplan pages with my SVG graphic and Markers and find the outcome a bit suboptimal. Especially as the Marker is not staying in position while zooming, font size is changing whild zooming …

So I basically got this Picture:
Heizung

and would like to update the temperatures, valve-, burner-, pump-status and remaining volume in the tank.

Unfortunately I have only found a tutorial for the Habpanel and this is not working within Floor Pages.
Hence my questions are:
Is there a way to update the SVG based on CSS / inline code like Habpanel on a Floorplan Page?
Or is there a way to have a Habpanel page as a Widget within the Pages?
Or is there a way with Markers and have them staying in place and style while zooming?

Nearly forgotten - later I would like to add some animation also. Don’t know yet if I would need some additional scripts or if there are ready to use classes available. But I don’t want to run in a direction with a dead end :slight_smile:

Thanks for your ideas and help.

I know what you mean. The floorplan has some shortcomings with zooming and the like. There is an alternative for the floorplan which is the Layout Pages | openHAB which even allows you to add widgets.

I have a complex floorplan which I started to migrate to the fixed canvas layout and so far the result is promising.

Btw, I do animations with custom icon-SVG’s that contain “@keyframes

hope that helps…

If you are on the latest milestone or newer snapshot then you can pretty easily build the entire svg in a widget. That lets you 1) change any svg parameter in response to item events and 2) add it to any page you like or just make it the only widget on a page with a simple layout.

The trick is that widget components can now be directly set to html or svg tags. See the updated docs for a description:

It’s also possible to build svg widgets on older versions, but it takes one additional little trick. See here for an example:

Thanks for the hint!

Actually I instantly upgraded to the latest milestone and tested the new components.
It look really promissing - but I have troubles to update the style of a selector in the stylesheet.

I tried this:

    - component: object
      config:
        stylesheet: "=(items[props.item_Kesselpumpe].state == ON)?'path#pump_Kessel {fill: green !important;}':'path#pump_Kessel {fill: yellow !important;}'"
        data: "=(props.SVG_file) ? (props.SVG_file) : '/static/images/Heizung.svg'"

but the expression is just added into the html head as is.

Second tried it hardcoded:

    - component: object
      config:
        stylesheet: |
          path#pump_Kessel {
            fill: green !important;
          }
        data: "=(props.SVG_file) ? (props.SVG_file) : '/static/images/Heizung.svg'"

doesn’t work either.

I tried it in the DevTools and applied the style to the inxpector - css and found out that I am not able change the style when I am adding the class to the HTML scope but it works when I am within the “#document” scope within the object tag.

There seems to be a boundary between the html document and the svg object.
Is there some way to overcome this boundary?

This is expected behavior. The stylesheet property is not processed by the expression parser so you cannot use expressions that way. See here for a way to get dynamic information into the stylesheet:

To my knowledge, this is only true when an svg is included in a document as an image, not when the svg is built directly into the html.

There should be no problem using the stylesheet to adjust svg parameters. Here’s a quick example:

uid: svg-style
props:
  parameterGroups: []
  parameters: []
tags: []
component: f7-card
config:
  title: Make it green
  stylesheet: >
    #starblob > #five-blob {
      fill: green;
    }
slots:
  default:
    - component: svg
      config:
        height: 48
        id: starblob
        viewBox: 0 0 24 24
        width: 48
        fill: yellow
      slots:
        default:
          - component: path
            config:
              id: five-blob
              d: m 19.38,23 c -5.27,3.88 -0.73,-7.01 -7.28,-6.97 -6.55,0.04 -1.86,10.88 -7.19,7.06 -5.32,-3.81 6.44,-2.86 4.38,-9.08 -2.07,-6.21 -10.92,1.59 -8.94,-4.65 1.98,-6.24 4.71,5.24 9.98,1.36 5.27,-3.88 -4.89,-9.89 1.66,-9.94 6.55,-0.04 -3.53,6.1 1.8,9.91 5.32,3.81 7.9,-7.7 9.96,-1.49 2.07,6.21 -6.89,-1.47 -8.87,4.77 -1.98,6.24 9.77,5.13 4.5,9.02 z
              fill: red
          - component: path
            config:
              id: three-blob
              d: m 21.63,22.5 c -5.81,4.92 -2.25,-4.4 -9.41,-6.97 -7.16,-2.57 -10.34,6.88 -11.69,-0.61 -1.35,-7.49 4.93,0.25 10.74,-4.66 5.81,-4.92 -0.79,-12.39 6.38,-9.82 7.16,2.57 -2.69,4.14 -1.33,11.63 1.35,7.49 11.13,5.52 5.32,10.43 z
              fill: orange

Results in
image
So the selector has correctly identified the five-pointed path and overridden its fill color with green.

Without seeing your whole code, I can only make a few guesses as to what’s going wrong. In this case, my first guess is that your selector is not correct. (DO note, that most of the time when you change a stylesheet property you will not see the results until you press the Redraw button at the bottom of the editor.)

1 Like

you are right - if I would rewrite my SVG code from Inkscape to follow the YAML syntax that would work. Unfortunately my SVG is a little bit larger than a icon and rebuilding it from scratch, having no editor (like Inkscape) would really errorprone and lots of work.

CSS inherritance does not work if I use the object-tag to load the SVG from a file as the browser expects the css within the SVG file.

I currently playing around with using the SVG / USE tag from the icon-Sets example you postet earlier. Unfortunately I don’t get the SVG loaded from the href and it seems also not to work.

At the moments there are more blockers then enablers in the MainUI to have something dynamic or animated on the UI :frowning:

Either I can load a SVG picture but not apply any styles due to browser restrictions
nor can I apply the styles within the SVG as I am not able to run JS to change the class of a path within the SVG object.

Having dynamically created styles or JS would be the next issue to think about.

I created a simple example of what I would like to do:

This is the widget:

uid: TEST_SVG_widget
tags: []
props:
  parameters:
    - description: Path to the SVG-file
      label: Source of SVG file
      name: svg-src
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Nov 24, 2022, 7:29:52 PM
component: f7-block
config:
  style:
    --state-color: "=(items[props.item].state == 'ON')? 'green !important;' : 'yellow !important;'"
  stylesheet: >
    .test {
      fill: var(--state-color)
    }
    path#pump_Waermetauscher_FBH {
      fill: var(--state-color)
    }
slots:
  default:
    - component: div
      config:
        content: test
    - component: f7-row
      config:
        tag: svg
        style: display:none
      slots:
        default:
          - component: f7-row
            config:
              tag: use
              href: =(props.svg-src || '/static/images/test.svg')
              height: 100px
              width: 100px

and this is the test.svg:
Basically a simple icon added in a new document in inkscape

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="7.2406502mm"
   height="7.0417399mm"
   viewBox="0 0 7.2406502 7.0417399"
   version="1.1"
   id="svg927"
   inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"
   sodipodi:docname="test.svg">
  <defs
     id="defs921" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="11.2"
     inkscape:cx="21.790757"
     inkscape:cy="16.369268"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     inkscape:document-rotation="0"
     showgrid="false"
     fit-margin-top="1"
     fit-margin-right="1"
     fit-margin-bottom="1"
     fit-margin-left="1"
     lock-margins="true"
     inkscape:window-width="1254"
     inkscape:window-height="906"
     inkscape:window-x="82"
     inkscape:window-y="0"
     inkscape:window-maximized="0" />
  <metadata
     id="metadata924">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Ebene 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-102.21301,-144.6458)">
    <path
       fill="currentColor"
       class="test"
       d="m 108.45366,150.68754 v -1.68059 h -0.39305 a 2.4369015,2.6049025 0 0 0 0.131,-0.84028 2.3582917,2.5208732 0 0 0 -2.35828,-2.52087 h -2.62032 v 1.68058 h 0.39304 c -0.0839,0.26329 -0.13102,0.54618 -0.13102,0.84029 a 2.3582917,2.5208732 0 0 0 2.3583,2.52087 h 2.62033 m -0.7861,-2.52087 c 0,0.35852 -0.0891,0.69184 -0.24631,0.98034 l -0.90663,-0.5602 c 0.0655,-0.12323 0.10481,-0.26609 0.10481,-0.42014 0,-0.18206 -0.0551,-0.35012 -0.14674,-0.48738 l 0.85424,-0.65262 c 0.20962,0.32211 0.34063,0.71985 0.34063,1.14 m -1.83423,1.96068 c -0.67867,0 -1.27086,-0.39495 -1.58791,-0.98034 l 0.90663,-0.5602 c 0.13626,0.25208 0.39043,0.42014 0.68128,0.42014 h 0.0759 l 0.0996,1.112 -0.17556,0.008 m 0,-2.80097 c -0.31706,0 -0.59219,0.20168 -0.71536,0.49298 l -0.95379,-0.46497 a 1.8326547,1.9589986 0 0 1 1.66915,-1.14839 v 1.12038 m 0,0.5602 c 0.14412,0 0.26203,0.12604 0.26203,0.28009 0,0.15407 -0.11791,0.2801 -0.26203,0.2801 -0.14412,0 -0.26204,-0.12603 -0.26204,-0.2801 0,-0.15405 0.11792,-0.28009 0.26204,-0.28009 z"
       id="pump_Waermetauscher_FBH"
       style="display:inline;stroke-width:0.270915"
       inkscape:label="pump_Waermetauscher_FBH" />
  </g>
</svg>

What I would like to achieve is to control the fill propperty of the path. Either via a dynamic stylesheet or via JS.

Current issues with that test widget:

  1. the SVG is not loading from the file
  2. the variable for the stylesheet shows just the variable name in the header

Ultimately, what you are trying to do is a complex process. There isn’t really going to be a solution that doesn’t include quite a bit of work on your part. From my experience with the MainUI the way to achieve what you want with the least work is to build the svg in the page. An svg file is just xml tree you can edit it in any text editor to remove all the extraneous bits that inkscape adds in, reduce it to it’s required fundamental skeleton and then a couple of global search and replaces and you’re probably most of the way there.

There are other ways to get there too: you could make a background image without the parts that need to be dynamic text and then manually position text elements where you want them and work with masks and filters to try and get the color changes you want or you could re-render the entire thing within a <canvas> element. These other options would be many times more work, I suspect.

I’m fairly certain that even though use can bring in external files, you still can’t access individual parts of those files for styling.

I don’t even think JS helps you here, it all a matter of whether or not you can even get to the pieces of the svg you want to control and that’s down to what’s a part of the DOM and what isn’t.

I would argue that there is a huge collection of ways to make the MainUI animated and dynamic, just not the one you are attempting at the level of complexity of your particular usecase.

What you want only doesn’t exist because no one has yet decided that the effort to make it happen is worth the result. If the result is that valuable to you then you could put out a bounty for it, or better yet, you could easily be one to add it to the project…build a script to auto-convert svg files into widget yaml syntax…add a component that parses an svg file into the page html…

Hi @JustinG & @litronics @JamesC

I’ve been picking at these examples to build some dynamic layouts using rects and paths…

But I can’t figure out how to drop a bit of text using the same process onto a dynamic layout.

Or home-grown SVG icon for that matter.

Do either of you have an example? Here is what I have working and what I’d like to get working:

          - component: rect
            config:
              id: area-livingroom
              width: 150
              height: 160
              x: 40
              y: 520
              fill: yellow
              opacity: =Math.round(items.gLight_LivingRoom.state)/100
              stroke: black
          - component: rect
            config:
              id: area-bedroom
              width: 150
              height: 140
              x: 40
              y: 380
              fill: yellow
              opacity: =Math.round(items.gLight_Bedroom.state)/100
              stroke: black

As an alternate approach, I can isolate this power indicator from this a power widget so that it displays a simple SVG animation on my layout:

uid: SVG_Temperature_Marker
tags: []
props:
  parameters: []
  parameterGroups: []
timestamp: Jan 1, 2023, 1:47:55 PM
component: f7-card
config: {}
slots:
  default:
    - component: f7-block
      config:
        style:
          background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><path stroke="red" stroke-width="2" id="grid_import" d="m80,5l60,0" opacity="undefined" stroke-linecap="undefined" stroke-linejoin="undefined" fill="none"/><circle r="5" fill="red">    <animateMotion dur="5s" repeatCount="indefinite" path="m80,5l60,0" />  </circle> </svg>')
          border-style: solid
          border-width: 0px
          height: 10px
          left: 0px
          margin-left: 50px
          margin-right: auto
          margin-top: auto
          position: absolute
          right: -10px
          top: 10px
          transform: translate(-50%, -50%)
          width: 280px

but when I substitute my own SVG I can’t get it to show anything - I have a feeling it has with all the different margins and sizes, but can’t figure it out for the life of me.

Any basic examples would be hugely appreciated!

In svgs text is just added with a text tag. The only trick there is that the text to be displayed needs to be inside the tag. This is accomplished with the new content property when using html component (see the Creating personal widgets link above).

You can see an very simple example of this with text here:

1 Like

@JustinG - perfect! I think this is just what I need, but couldn’t seem to locate! Will test shortly… thanks and Happy New Year!