Yet Another SH3D Floor Plan Plugin

Hey all,

I’ve seen some very cool projects others have done for displaying a live floor plan in HA with active lighting and I was hooked! I started modeling my apartment in SH3D and was ready to start rendering the floor plan.

I started out with creating the renders and manually creating the transparent PNG overlays but I quickly ran into some issues:

  • I really sucked at properly creating the transparent overlay images
  • Rooms with multiple light sources were even worse as I attempted to use different levels of transparency so the lights could combine together but this never came out quite right for me and the lights always seemed washed out
  • Every so often, my wife likes to move things around so repeating this process every time was not really an option

My next plan was to create a plugin for SH3D that would simply render all of the possible lighting combinations but I quickly found out that’s not feasible. Even with just 10 lights, that’s 16K renders at ~1.5 minutes each at my 1024x576 target.

Finally, I set out to combine both approaches and automate creating the image overlays by comparing a base image (with the lights off) to another image (with one light on) and saving only the difference as a transparent overlay image. This produced rather nice results but caused issues in rooms with multiple (independent) light sources. To overcome this, I decided to render all lighting combinations, but limit this per-room. This means that if two rooms don’t have any “light bleed” between them, I don’t really need create renders that overlap and, that way, I won’t reach the exponential number of rendering combinations. I reached a point where, for my existing 14 lights, I only need to render 18 images. Since most only have one light source, the rendering is also simplified and it took less than 15 minutes to render them all.

With rendering out of the way, I now wanted to add the option of tapping the floor plan to toggle a light. Sure, this can be done manually by pin-pointing every light, find the offset (in percent :person_facepalming:) from the corner and slap in the Lovelace panel YAML, but where’s the fun in that? This part took me way longer to get right than I’d like to admit, but the plugin now calculates where each light source is rendered, according to the viewing position and angle, and adds the light’s state-icon at the relevant position. If a switch light has multiple source, e.g., spot lights, I just calculate the center of all the lights and place the icon there.

To wrap it up, before publishing, I added a simple UI to display and modify some configuration values and it’s now ready for release!

For example, using one of SH3D’s demo models:
demo
The generated floor plan images:


And the matching YAML:

type: picture-elements
image: /local/floorplan/base.png?version=1
elements:
  - type: conditional
    conditions:
      - entity: light.bedroom2_uplight
        state: 'on'
      - entity: light.bedroom2_spotlight
        state: 'off'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom2_uplight
          - light.bedroom2_spotlight
        image: /local/floorplan/light.bedroom2_uplight.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom2_uplight
        state: 'on'
      - entity: light.bedroom2_spotlight
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom2_uplight
          - light.bedroom2_spotlight
        image: /local/floorplan/light.bedroom2_uplight_light.bedroom2_spotlight.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom2_uplight
        state: 'off'
      - entity: light.bedroom2_spotlight
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom2_uplight
          - light.bedroom2_spotlight
        image: /local/floorplan/light.bedroom2_spotlight.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'on'
      - entity: light.bedroom_side2
        state: 'off'
      - entity: light.bedroom_desk
        state: 'off'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_side1.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'on'
      - entity: light.bedroom_side2
        state: 'on'
      - entity: light.bedroom_desk
        state: 'off'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_side1_light.bedroom_side2.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'on'
      - entity: light.bedroom_side2
        state: 'on'
      - entity: light.bedroom_desk
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_side1_light.bedroom_side2_light.bedroom_desk.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'on'
      - entity: light.bedroom_side2
        state: 'off'
      - entity: light.bedroom_desk
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_side1_light.bedroom_desk.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'off'
      - entity: light.bedroom_side2
        state: 'on'
      - entity: light.bedroom_desk
        state: 'off'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_side2.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'off'
      - entity: light.bedroom_side2
        state: 'on'
      - entity: light.bedroom_desk
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_side2_light.bedroom_desk.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bedroom_side1
        state: 'off'
      - entity: light.bedroom_side2
        state: 'off'
      - entity: light.bedroom_desk
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bedroom_side1
          - light.bedroom_side2
          - light.bedroom_desk
        image: /local/floorplan/light.bedroom_desk.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.kitchen
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.kitchen
        image: /local/floorplan/light.kitchen.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.bathroom
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.bathroom
        image: /local/floorplan/light.bathroom.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.living_room_uplight1
        state: 'on'
      - entity: light.living_room_uplight2
        state: 'off'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.living_room_uplight1
          - light.living_room_uplight2
        image: /local/floorplan/light.living_room_uplight1.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.living_room_uplight1
        state: 'on'
      - entity: light.living_room_uplight2
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.living_room_uplight1
          - light.living_room_uplight2
        image: /local/floorplan/light.living_room_uplight1_light.living_room_uplight2.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.living_room_uplight1
        state: 'off'
      - entity: light.living_room_uplight2
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.living_room_uplight1
          - light.living_room_uplight2
        image: /local/floorplan/light.living_room_uplight2.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: conditional
    conditions:
      - entity: light.corridor
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: none
        hold_action:
          action: none
        entity:
          - light.corridor
        image: /local/floorplan/light.corridor.png?version=1
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%
  - type: state-icon
    entity: light.bathroom
    title: null
    tap_action:
      action: toggle
    style:
      top: 44.76%
      left: 25.65%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.kitchen
    title: null
    tap_action:
      action: toggle
    style:
      top: 24.80%
      left: 54.42%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.bedroom2_uplight
    title: null
    tap_action:
      action: toggle
    style:
      top: 28.89%
      left: 44.64%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.bedroom2_spotlight
    title: null
    tap_action:
      action: toggle
    style:
      top: 27.47%
      left: 27.78%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.living_room_uplight1
    title: null
    tap_action:
      action: toggle
    style:
      top: 26.37%
      left: 74.10%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.bedroom_side1
    title: null
    tap_action:
      action: toggle
    style:
      top: 66.97%
      left: 22.39%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.living_room_uplight2
    title: null
    tap_action:
      action: toggle
    style:
      top: 58.60%
      left: 75.59%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.bedroom_side2
    title: null
    tap_action:
      action: toggle
    style:
      top: 83.78%
      left: 21.18%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.corridor
    title: null
    tap_action:
      action: toggle
    style:
      top: 55.01%
      left: 38.45%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)
  - type: state-icon
    entity: light.bedroom_desk
    title: null
    tap_action:
      action: toggle
    style:
      top: 73.32%
      left: 38.64%
      border-radius: 50%
      text-align: center
      background-color: rgba(255, 255, 255, 0.3)

You can find some more information, and the plugin itself for download at: GitHub - shmuelzon/home-assistant-floor-plan: Home Assistant Floor Plan Generator Plugin For Sweet Home 3D

I’d be happy to hear any feedback, and thanks to everyone for the inspiration to do this!

8 Likes

There are awesome projects for floorplan, but I recently deleted all my floorplan attempts and config, because I just find it way too fiddly and time-consuming. Your project means I just may try it again.

1 Like

Hi there,

nice Project, helps a lot. i only have one issue.

the state-icons remains in the buttom right corner and not in place over the lamp.

  - type: state-icon
    entity: switch.sonoff_kueche
    title: null
    style:
      top: 72,26%
      left: 55,02%
    tap_action:
      action: toggle

It has a yaml, but the icons still is in the corner. I tried around but was not able to find a solution.

Hey,

What happens if you change the commas in the top and left fields of the style to dots?
It may be that they’re not parsed correctly because the decimal separator is matching your locale.

1 Like

Then it works like it should!

Thank you so much!

Glad to hear it worked out!
I think I have a fix for that ready. Would you mind trying it out and see if the YAML now includes the correct decimal separator?

https://github.com/shmuelzon/home-assistant-floor-plan/actions/runs/8070526884/artifacts/1280051716

Thanks in advance!

1 Like

The fix works like a sharm. no need to RegEx for replace anymore :wink:

1 Like

Nice! Thanks for the confirmation!
I’ll merge the fix and release it

Feature Request:
Hey shmuelzon, is there a way to integrate windows open / close So that the winow is open or closed depending on the status?

Hey,

That’s an interesting one :slight_smile:

First of all, I suggest opening an issue/feature request on Github so we can easily track this.

I tried to understand how these openings are configured in SH3D and I think I know how to control them but I’m not sure which images to generate from them.

For the easy part, I will need the design to include the “open” state since, otherwise, I won’t know how to move the door/window pane, in which direction nor how much and the plugin will use that as the initial state. The plugin can use that s the open state and reset the door/window openings to be the closed state. This is similar to how the design currently includes the lights’ power values and the plugin sets that to zero in order to turn off and restores the initial value to turn back on.

Then, for each room that has a dynamic opening, I will need to render the door/window both as open and as closed along with all the light combinations. In fact, I might need to do this for the two rooms on either side of the opening, unless one side is “outside” and doesn’t have any lighting of its own. Then again, there’s always the sun light that might affect the render.

Finally, the most problematic part is the base image on which I overlay the different states. Right now, it has all the lights turned off. I need to see how it will look when overlapping the base image with one that has the opening open without lights and another that has it open with lights. It might look good enough but I just don’t know what to expect yet. I think the main problem would be the interaction the opening state may have on the lights coming from adjacent rooms but I’m willing to give it a try.

I’m assuming, from Home Assistant’s perspective, these would be binary sensors that say if the opening is open or not?

2 Likes

Thank you, this is really an amazing project, so usefull !!!
I sucessfully generated my floorplan with lights, including many lights per room :slight_smile:

→ I also have binary sensors indicating presence in a room. Is it possible to include it ? al least partially (I can finish modifying the yaml if necessary).
I tried to include the binary_sensors on person and other SH3D entities but only light seem to be recognized.

→ also for my mobile dashboard version, I need to disable the lights and instead generate some links to the rooms page. How would you do that ?

Thank you so much :slight_smile:

Hey, I’m glad it helped you with your setup.

I also have binary sensors indicating presence in a room. Is it possible to include it ?

Are you using the latest version of the plugin? Binary sensors have been supported since v0.2.0. You just need some piece of furniture that’s named binary_sensor.xxx and the plugin will place it on the floor plan with a state icon.

I need to disable the lights and instead generate some links to the rooms page.

If I understand correctly what you’re trying to achieve, you’ll need to take the generated YAML and remove all the state-icons it generates at the end. Then for all the rooms you’ll need to change the tap_action from none to navigate and add the relevant navigation_path to the matching room. See Actions - Home Assistant.
I think this will only work when using the plugin in the “room overlay” mode (which was the initial implementation) and not the new CSS mode,

Good luck!

Indeed, it’s working fine. I was expecting to see it with the binary sensors in the same screen as the lights, my mistake.

Do you know how I could make the state-icon of the binary sensors transparent when inactive ?
This is probably in the style css attribte but I don’t know how to differenciate the state icon when active/inactive

You might be able to hide the state icon by adding “conditions” to it’s YAML entry, much like how the conditions specify if each overlay image should be visible according to the light state

Yes! of course, you"re right, thanks !!

I have just a little problem. When I render in CSS mode, everything is fine but in “room overlay” mode, there is a strange behaviour:
It displays correctly the lights on/off on the icons. But only one room at a time can be graphically illuminated. This is what happens:
My office room display the light graphicaly. When I turn its light off, the graphic show a dark room and at the same time the living room turn clear (its light was on). If I turn on the WC, nothing happens in the graphic untill I turn off the living off and the WC turns on. Do you understand this behaviour?

EDIT: I can see there is a second folder of rendered files that I had not seen.

  • a folder of globally darker images in render/, and
  • a folder of lighter images in render/floorplan/
    Both were generated at the same time (from 14:21 to 14:51).
    Is it normal ? How should I use it ?

EDIT2: I replaced the files in “render” with the ones in “floorplan” and I postfixed all the image names with “&v=5” to get rid of cache issues.
The problem seem fixed. The result is not as good as in CSS mode, I can see that in some rooms some dark pixels remain over the light image:

You are correct.

  • I removed all the “state-icons” entries for the lights → the light icons disapeared
  • I made the “state-icons” for the motion sensors conditional and changed the icon sucessfully → now the icons appear only when there is a motion in the room

But when I try to add a “navigate” “tap-action” it doesn’t work. I quickly tried in CSS mode and it does’t work as you predicted, but I’m now trying in “room overlay” and nothing happends… Any idea ?

  - type: conditional
    conditions:
      - entity: light.fan_bureau
        state: 'on'
    elements:
      - type: image
        tap_action:
          action: navigate
          navigation_path: /dashboard-home/bureau
        hold_action:
          action: none
        entity:
          - light.fan_bureau
        image: >-
          /local/floorplan/light.fan_bureau.png?version=366806055219FE4ACD647CF4DA39D8FA&v=5
        filter: none
        style:
          left: 50%
          top: 50%
          width: 100%

Hey, as you’ve already figured out, you need to copy the images from the floorplan directory to your HA installation. The renders directory is simply to cache the images SH3D generates so you won’t need to wait a long time for them if you want to modify some configuration.
In CSS mode, the floor plan images are the same as the renders but in the room overlay mode, the code tries to find the difference between each render and the base image so only the changed pixels are saved and the rest of the image is transparent. This isn’t perfect and there is noise in the form of the black pixels you see there. I’m sure there are better ways to detect difference between two images but my current implementation is very naive. You can try to change the sensitivity value a bit to see if it helps clean up the image. Since the original renders are cached, it should be a quick process :slight_smile:

As for the browser cache, the YAML includes a link to the image with an MD5 hash, so it shouldn’t be a problem.

Regarding the navigation, I have no idea. The CSS mode includes complete images so I assumed only the top-most would get the tap action but with the room overlay I thought that maybe it would ignore taps on the transparent parts and would “drill down” to the visible layer which would be the room. Thinking about it now, the room specific layer would only show if the room has a light turned on, otherwise you would only see the base image and you can’t tap that to navigate to a specific room. Can you check if the navigate action works when there is a light turned on in the room?

Yes, there is a strange behaviour. The last light that was turn on drive the “navigate” destination. If the office light is the last turned on, then a clic anywhere in the floorplan will navigate to the office. Then if I turn on the room, navigation to the room…

@shmuelzon: do you think it would be possible to add a transparent layer including zones with navigate proprerties ?

On a tablet I leverage the possibilites of the floorplan.

And thanks to you I also have a wonderful floorplan on my mobile that displays a small view of the house with the active lights and movement detected, but it’s too small to add more.
I’ve put it on the first page of my mobile dashboard and it would be really usefull to point a specific room I want to navigate to. Just need to activate the tap actions.

If you have any suggestion, idea… it will be more than welcome :slight_smile:

Hey, I’m not sure how to technically do that with the HA picture elements card. I think I know how to calculate the coordinates/borders of each room but the question is what then. HTML used to have an image map but I’m pretty sure that’s no longer used anywhere. If I add a transparent image of a room with a link, it would need to be an image with the resolution of of the room but then, I’m limited to squares so I won’t be able to get the exact layout of the room. Then, there’s also the question of whether transparent images are even clickable in the browser or if it ignores the transparent parts and then clicks will be sent to the elements below the image. I’m also curious regarding the scaling of images in the browser. Since, right now, all the images are the same size, they all line up, so it’s not a problem. But, if I use smaller images for each room, they might not scale the same way.

If transparent pixels are ignored for clicks, then it might be easier because then I can create full size images with only the overlay of a room with something close to transparent that will, hopefully, be the clickable part.

If you have time to play around with this and find something that works, I can try to integrate it to the plugin.