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!

4 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?

1 Like