How to add actions (analyzer, popover) to a widget with a single f7-block and only svg elements

Hi.

I am currently adopting the fronius energy widget [OH 4.2] Fronius inspired energy flow widget [4.2.1; 5.0.0] - my full current code can be found here: OpenHab/Widgets/fronius_energy_flow_v2.yaml at main · daabm/OpenHab · GitHub (will release it to the market place, of course).

To break it down - this is one of the icons defined as svg (partially).

                            - component: svg
                              config:
                                comment: inverter icon
                                height: 44px
                                stroke: dimgray
                                stroke-miterlimit: 10
                                viewBox: 0 0 70 70
                                width: 44px
                                x: 128px
                                y: 128px
                              slots:
                                default:
                                  - component: rect
                                    config:
                                      fill: none
                                      height: 64
                                      rx: 6.8
                                      stroke-width: 2px
                                      width: 58.7
                                      x: 5.65
                                      y: 3

I now want an analyzer popup item for the icon area, but have no clue how to… The svg component itself ignores action: analyzer. And adding any oh- or f7- elements breaks the layout. Any help greatly appreciated! :slight_smile:

BTW: The original widget used svg files for icon elements. I converted that to be fully self contained and do some trickery with dynamic changes like a rotating heatpump vent and a fading sun.

Correct. Only oh- components accept the action configurations.

Without seeing the full configuration you’re working with, it’s hard to say what the best way to do this would be. From the snippet you’ve provided it looks like the best option is to just put that svg icon inside an oh-link:

- component: oh-link
  config:
    action: analyzer
    ...rest of config
  slots:
    default:
      - component: svg
        ...rest of svg

Another good option, depending on the rest of the structure might be to take advantage of SVG’s foreignObject element:

Full widget code is in the above linked GIT repo :slight_smile: Didn’t want to post it here because it only helps readers who are willing to dive into it a bit.

If I add an oh-link, how would this component know where it is? I have no grid/rows/cols, it’s all a single f7-block with a viewbox. The only elements that have any “coordinates” are the SVG ones, and the SVG element in the excerpt above doesn’t even have a parent with coordinates. It is simply placed in the center of its surrounding element by viewbox positioning in the f7-block.

I admit I am quite a noob in terms of SVG.

Sorry, missed that.

You are correct, the basic oh-link option only works by placing the entire SVG inside the link (with placement being determined by whatever determined the placement of the SVG in the first place). I see now that you are asking about sub SVG elements, in which case the foreignObject method mentioned above is the only best way for you to go.

The foreignObject element is just like any other svg element in that it takes the x, y, height, and width attributes for placement. It is unlike other SVG elements in that it essentially becomes a new region for you to put any html inside your SVG. In this case, that native html would be an oh-link. Because an oh-link can hold any other component you can then but your full SVG block for an icon in the oh-link and then the link encompass the entire icon.

For the example you posted of the sub SVG for your inverter icon. The position and size attributes that determine where the icon is should now, instead determine where the ‘foreignObject’ goes.

- component: foreignObject
  config:
    height: 44px
    width: 44px
    x: 128px
    y: 128px

Now inside that you can render a basic oh-link that gets you access to the oh actions such as analyzer:

- component: foreignObject
  config:
    height: 44px
    width: 44px
    x: 128px
    y: 128px
  slots:
    default:
      - component: oh-link
        config:
          action: analyzer
          actionAnalyzerItems: YOUR ITEM NAME(S) HERE

Then you put the svg icon elements inside the oh-link, setting the SVG size to just fill the container 100% (which is the oh-link and then by extension the foreignObject which has the size and position that you want).

- component: foreignObject
  config:
    height: 44px
    width: 44px
    x: 128px
    y: 128px
  slots:
    default:
      - component: oh-link
        config:
          action: analyzer
          actionAnalyzerItems: YOUR ITEM NAME(S) HERE
        slots:
          default:
            - component: svg
              config:
                comment: inverter icon
                stroke: dimgray
                stroke-miterlimit: 10
                viewBox: 0 0 70 70
                height: 100%
                width: 100%
              slots:
                default:
                  - component: rect
                    ....rest of icon code

Works like a charm - thank you sooo much! :heart_eyes: At least in the browser interface.

In the iOS app, the layout goes nuts. In portrait mode, the oh-link/icon are invisible (behind some other element, I assume). In landscape mode, they become visible but are positioned wrong.

Layout in browser, mouse on the heatpump icon so it gets dimmed a bit, popover working:

Layout on iOS portrait, “icon where are you?”:

Layout on iOS in landscape, icon at wrong position but popover working:

I have no idea what goes wrong here, and I don’t know how to analyze it :frowning:

This is the changed code:

                            - component: foreignObject
                              config:
                                comment: heatpump icon area
                                height: 44px
                                width: 44px
                                x: 428px
                                y: 228px
                              slots:
                                default:
                                  - component: oh-link
                                    config:
                                      action: group
                                      actionGroupPopupItem: kermi
                                    slots:
                                      default:
                                        - component: svg
                                          config:
                                            comment: heatpump icon
                                            fill: =fn.switch_color(#props.power_heatpump, "heatpump")
                                            height: 100%
                                            viewBox: 0 0 490 490
                                            width: 100%

(remaining svg definition left out…) Full code at OpenHab/Widgets/fronius_energy_flow_v3.yaml at c35932e184b5ec83e1e000582472ff9e867c8d21 · daabm/OpenHab · GitHub
I only changed the heatpump for now, added foreignObject and oh-link and indented the svg properly.

Opened the browser interface in Safari on iOS - same symptom in both portrait and landscape. So it might be a safari rendering issue, but beyond my capabilities in diagnosing further.

I know that there are some well-known Safari SVG rendering issues, but I do not know the complete list as I do not work with Safari if it can be avoided. According to the doc link above, foreignObject should have full Safari support on both desktop and iOS.

On the desktop Safari you have a chance. On my troubleshooting tutorial, I go over some browser tools that help you know check out what’s wrong with widgets. In this case the first thing to find out is whether the icon is 1) not being rendered, 2) being rendered in the wrong place, or 3) being rendered at the wrong size. My gut feeling, based on other Safari SVG issues and what you show above, is that this is an instance of #2. Once you can narrow it down to one of those three options it’s usually fixable.

Being rendered in the wrong place is correct.

Looking at the html (dev tools) doesn’t provide me any insight, and I don’t have a desktop safari available (all desktops running windows, and safari isn’t available for this OS anymore). Stuck now, dead end :frowning: (my wife will not accept this weird looking widget… WAF broken.)

<foreignObject comment="heatpump icon area" height="44px" width="44px" x="428px" y="228px" class=""> <a class="link" href="#" action="group" actiongrouppopupitem="kermi"><!----><!----><svg comment="heatpump icon" fill="grey" height="100%" viewBox="0 0 490 490" width="100%" class=""> <path d="M244.99,211.903c-7.128,0-12.907,5.774-12.907,12.902c0,7.134,5.779,12.915,12.907,12.915 c7.127,0,12.912-5.781,12.912-12.915C257.901,217.677,252.116,211.903,244.99,211.903z" class=""> <!----></path><title content="Standby
COP: 0
Flow temp: 42,5 °C" class="">Standby
COP: 0
Flow temp: 42,5 °C <!----></title></svg></a></foreignObject>

(Removed most of the elements for simplicity)

Yep, it turns out this a Safari bug that was reported 17 years ago and never fixed…:person_shrugging:

There are a few suggestions on that page. For example, you can try adding

style:
  position: fixed

to the oh-link and see if that helps.

If none of the workarounds for this bug still work, then you can resort to the other option. This is more of a hack than a native OH widget action but you can use javascript to access the function of the on-link. I don’t think there are any Safari shenanigans that negate this option…but, you never know.

So, for this you would get rid of the foreignObject and the oh-link in the middle and restore your SVG to its original arrangement. Then you would put the oh-link by itself somewhere outside of the SVG, for example, as a child of the f7-block. The trick is to make sure you set the display: none style on this link and give the link a unique ID:

- component: f7-block
  config:
    style:
      content-justify: center
      display: flex
      height: 100%
      padding: 5%
      width: 100%
  slots:
    default:
      - component: oh-link
        config:
          id: heat-pump-analyzer
          action: analyzer
          actionAnalyzerItems: YOUR ITEM NAME(S) HERE
          style:
            display: none
      - component: svg
        ...rest of SVG

Note: the SVG is a sibling of the link, not a child.

Now, inline SVG elements will accept event attributes so to your heat pump icon svg, you can just add

onclick: document.getElementById('heat-pump-analyzer').click()

Now, clicking on your heat icon will trigger the action of the link.

Turns out we both missed the correct - and easy - way to do it… I am not too familiar with the widget object model nor with svg, and you maybe simply overlooked it.

- component: svg
  config:
    comment: heatpump icon area
    height: 44px
    width: 44px
    x: 428px
    y: 228px
  slots:
    default:
      - component: oh-link
        config:
          action: group
          actionGroupPopupItem: =props.popup_heatpump
        slots:
          default:
            - component: svg
              config:
                comment: heatpump icon
                fill: =fn.switch_color(#props.power_heatpump, "heatpump")
                height: 100%
                viewBox: 0 0 490 490
                width: 100%
              slots:
                default:
                  - component: rect
                    config:
                      comment: invisible rectangle to provide a clickable area for oh-link
                      height: 100%
                      width: 100%
                      opacity: 0

The svg component accepts a oh-link child. My fault in the first place was that I then made the icon a sibling of oh-link and not a child in its default slot… (move the svg icon component 3 indents to the left, then the icon vanishes behind the oh-link). Only trick here is to add an invisible rect to the icon to react to the oh-link - otherwise, only “filled” svg components are clickable.

Anyway, talking to you finally got me to a solution, it works like a charm now :slight_smile:

Big THANK YOU!

1 Like

You’re right, I didn’t think that would work in this case for reasons that were clearly incorrect. I’m glad you tried it and proved me wrong! Now I know.

Purely by accident… Was in the progress to try the javascript variant, changed the foreignObject back to SVG. Then - dunno why - saved, reloaded and smiled :slight_smile: