When I started looking at a Smart Home controller for my new house, something that struck me as being quite 'cool' (if you're a geek like me) was a 3D floorplan with interactive icons. When I settled on Home Assistant, there were several people showing implementations with simulated lights turning on and off and switching between day and night. So, I decided to create an interactive floorplan for my home, but I also wanted to simulate the amount of daylight in the image and add other elements, like motion sensors, and coloured lights, etc. Below is the Ground floor as a work in progress.
A few basic pre-requisites that you'll need (all of which have plenty of tutorials on how to set them up):
- Home Assistant up and running
- Home Assistant Community Store (HACS) installed in Home Assistant
- Config Template Card frontend add-on through HACS to enable JavaScript and CSS support
- A means to upload image files to Home Assistant (I use the Samba Share Add-on)
- Some smart lightbulbs, such as Philips Hue
- A light sensor (I'm using a Philips Hue motion sensor, which also gives you a thermometer and light meter)
- A software package to create your images (I used Sweet Home 3D to create the initial images and Paint.Net to manipulate them)
How this works...
The principle is that we will display a dark, 'night-time' image of the floorplan and superimpose other images on top of it in the Home Assistant Lovelace interface (the default UI). A 'daytime' image will be superimposed over the top of the night-time one during the day and we will change the opacity to simulate the light levels as read by the light sensor. Then, for the lights, we will overlay images on top and select the 'lighten' blend mode to change only the parts of the images that would be lit by the lights. We can also change the opacity of these overlays depending on the brightness of the lights and we can change the hue to match colour bulbs.
As this is all done with overlays, we need all the images to align perfectly. The easiest way I figured to do that was to create all the overlays the same size as the full image, but have a transparent background apart from the room I'm interested in. I'll explain this more later, but if your night-time image of the floorplan is 1920x1080 pixels for example, then every image should be that size.
Creating the floorplan
Here's the procedure to create this interface. The first step is to create all the images you will need:
- Model your home in your 3D package of choice - I suggest giving Sweet Home 3D a try and watching some of the many tutorials online. I did this by using an image of the plans of my house as a background image and then drawing over the top of it. It's actually very quick and easy as Sweet Home 3D allows you to draw a scale on the image and set the origin, so drawing over it gives you fairly accurate measurements.
- Add any furniture in, then add all your lights to the plans, but disable them so that they don't show (we'll turn them on in batches later to get multiple renders with different lights).
- Now arrange the point of view (POV) and store it! You will create a set of images to overlay one on top of the other, so the point of view needs to be identical. If you store the POV, you can easily return to it later.
- Now create a daytime render of your home by selecting 'Create photo...' and set the time to be noon. This will give you a nice bright, well-lit image.
- Create a second image at dusk, just after the sun has set (I used 20:30 for mine in my location). This should render a dark image, but with all the elements still visible. (I experimented with setting it to midnight and adding some other lights, but this wasn't very easy or successful for larger floorplans, so I switched to just rendering it at dusk.)
- Now you need to turn some lights on in your model and re-render the dark scene. Don't turn all the lights on at once, you need to turn them on in groups that you would normally control together, e.g. I turned my wall lights on together and banks of downlighters. You could re-render for each individual bulb, but for me life's too short for that!
- In order to make these additional images a bit smaller, you can load them into a drawing package, cut out the part that is lit and save it on a transparent background. This isn't strictly necessary, as we will use the lighten blend mode later. However, if you decide to follow what I did, you would end up with something like the below image as the overlay for the Utility Room.
- Now all that remains is to upload the image files to Home Assistant. I use the Samba Share add-on for HA so I can just copy them. They need to go into the \config\www\ folder in HA, but I created a subfolder called \floorplans\ under this directory to keep things tidy.
Lovelace UI in Home Assistant
Now we have the images, we can create the UI to display them...
- Go to the Configuration > Helpers page and add a new dropdown helper by clicking on the '+ ADD HELPER' button at the bottom of the page.
- I called this 'Floorplan Level', but call it whatever you want, but remember to add an option for each floor in your house (e.g. Basement, Ground, First, etc.)
- Now go to the Configuration > Lovelace Dashboards page and click the '+ ADD DASHBOARD' button at the bottom of the page to create a new dashboard.
- Navigate to your new dashboard and then edit it (click the three vertical dots in the top right-hand corner). Accept the fact that the dashboard won't automatically get updated and start with a blank dashboard.
- To create the dashboard above, I started with a Vertical Stack card, which consists of a Horizontal Stack card to hold the floor name and the location of the people at the top, then a Conditional Card for each level of the house (4 in my case), and another Horizontal Stack card at the bottom to hold the floor buttons so we can switch between floors. You don't need to add all of these, but if you're going to, it's best to do the structure first. Now we can create the floorplan for a single floor. I won't go through everything, as it's very repetitive, but you can just repeat the same tasks for each floor.
- Add a Conditional Card to your Vertical Stack card and set the condition to be when the input_select you created above is equal to the option (floor) you want. It should look something like below:
- In the 'Card' tab of the Conditional Card, add a 'Picture Elements' card. This can only be configured via yaml and not in a graphical way. Don't worry though, it's not that hard.
- You now need to add in the custom Config Template Card code (if you haven't installed this yet, you need to install it and restart the system first):
type: 'custom:config-template-card'
entities:
- sun.sun
- sensor.front_door_motion_sensor_light_level
- climate.kitchen
- binary_sensor.front_door_motion_sensor_motion
- light.utility_room
card:
type: picture-elements
image: /local/floorplan/Ground-Night.png
elements:
- type: conditional
conditions:
- entity: sun.sun
state: above_horizon
elements:
- type: image
entity: sun.sun
image: /local/floorplan/Ground-Day.png
tap_action:
action: none
hold_action:
action: none
style:
top: 50%
left: 50%
width: 100%
mix-blend-mode: lighten
opacity: >-
${ states['sun.sun'].state === 'above_horizon' ?
((states['sensor.front_door_motion_sensor_light_level'].state -
1000) / 2000) : 0 } - The above code adds the Config Template Card and then lists all the elements that you want to track. This ensures that whenever there is a state change on any of these entities the UI will be updated. You will need to add all the entities you want to display here (e.g. lightbulbs or groups and any other sensors).
- Then we add the Picture Elements card as the only card in the Config Template Card. We set the night-time image that we created above to be the main picture for this card and then start to add elements to this card. All of the remaining overlays and icons will be elements under this Picture Elements Card
- The first element we add is a Conditional element so that we only display the daytime image when the sun is above the horizon. This is a simple matter of using the sun.sun entity and checking if the state is 'above_horizon' and setting the sub-element to be the daytime image. (The style configuration of top and left set to 50% and width set to 100% just centres the image overlay and makes it the same size as the background image and we will set the same style for all the overlays with a blending mode of lighten).
- In order to simulate the amount of light outside, we set the opacity based on the light level from a Philips Hue motion sensor outside the front door. The 'state' of this sensor gives us a number of lux. I decided that anything under 1000lx was getting a bit dim and we'd probably want the lights on, so I don't show the daytime overlay if the light levels are below this. I also decided that if there are over 3000lx at my front door it's fairly bright and I want the daytime image to be at full strength. However, opacity runs from 0 (invisible) to 1 (fully visible), so my calculation ensures that 1000lx and below results in an opacity of 0 and 3000 or above results in an opacity of 1.
- Now we add overlays for each set of lights. Essentially we add a new Image element with the overlay image and set the opacity according to the brightness of the bulb (or group of bulbs). This means that we can simulate the amount of light in the room according to the brightness of the bulb. So, for example, the code for my Utility Room lights goes in a new element in the Picture Elements Card and is:
- type: image
entity: light.utility_room
image: /local/floorplan/Ground-Light-Utility.png
tap_action:
action: none
hold_action:
action: none
style:
top: 50%
left: 50%
width: 100%
mix-blend-mode: lighten
opacity: >-
${ states['light.utility_room'].state === 'on' ?
(states['light.utility_room'].attributes.brightness / 255) : 0 } - Next, if you have coloured bulbs, we can change the hue of the overlay to simulate that as well. Add the following filter code after the opacity above. Changing the hue of white doesn't work, so we have to do a bit of pre-work to get a change in the colour. So, here we reduce the brightness of the overlay, change it to sepia (a built in CSS filter that makes sure white isn't white anymore) then saturate it to 1000% to get a red colour instead of white. Then we can 'rotate' the hue round the colour wheel until we get to the colour of the bulb (I added a 12 degree adjustment to account for the slightly yellowy-orange of the dusk image so the colours more closely matched the bulb colour).
filter: >-
${ states['light.utility_room'].attributes.hs_color ?
"brightness(0.5) sepia(1) saturate(1000%) hue-rotate(" + (
states['light.utility_room'].attributes.hs_color[0] - 12) +
"deg)" : "none"} - Now do the same for all the other lights!
- The order of the overlays and icons is important as they stack one on top of the other. So, if you want to be able to click on the icons, they have to come after all the lighting overlays. The first icons that I put on are for the NEST thermostats (a similar thing should work for other thermostats, but the attributes might be different). For these I add a State Label element to my Picture Elements card to add text as below. This pulls the current_temperature attribute from the climate.kitchen entity (I have 7 heating zones in the house one of which is the open plan kitchen/diner at the back of the house). This time I want to place it in the room near where the actual thermostat is, so I set the top and left attributes accordingly. The border-radius of 50% just makes is round rather than square and I set a background-color so you can see the icons more easily. the color styling below makes the text slightly transparent white if the heating zone is off and orange if it is actually being heated. This way I can easily see which zones are heating very quickly.
- type: state-label
entity: climate.kitchen
attribute: current_temperature
suffix: °C
style:
top: 80%
left: 70%
border-radius: 50%
color: >-
${ states['climate.kitchen'].attributes.hvac_action === 'off' ?
"rgba(255, 255, 255, 0.85)" : "rgba(255, 165, 0, 0.85)" }
background-color: 'rgba(32, 32, 32, 0.5)' - Next up is an icon to show when a motion sensor has detected someone. To do this, I add Conditional elements for each motion sensor and an Icon element to show the actual icon. The concepts are exactly the same as before.
- type: conditional
conditions:
- entity: binary_sensor.front_door_motion_sensor_motion
state: 'on'
elements:
- type: icon
icon: 'mdi:motion-sensor'
style:
top: 40%
left: 6%
border-radius: 50%
padding: 0.6em
color: 'rgba(255, 255, 255, 0.85)'
background-color: 'rgba(32, 32, 32, 0.5)' - Finally we get to the lights! This time, instead of a simple Icon, we use a State Icon, which will change state (usually colour) depending on the state of the underlying element. The only difference here is that we link it to an actual element (in this case a lighting group consisting of two bulbs) and I have updated the tap_action to toggle the lights on/off when you click or tap the icon (the default behaviour is 'more information'). A long press on the icon will bring up the full control so you can change brightness, colour, etc.
- type: state-icon
entity: light.utility_room
tap_action:
action: toggle
style:
top: 80%
left: 43%
border-radius: 50%
background-color: 'rgba(32, 32, 32, 0.5)' - Once you have added all the icons you want add a new Conditional card to the Vertical Stack and make it conditional on your input_select being set to another floor. Then create the floorplan in exactly the same way as above.
- To get the buttons along the bottom I added a new Horizontal Stack card and added 5 cards to it: the first is a Markdown card to display some text and then there are 4 Button cards, one for each floor in my house. The setup for each of them is basically the same, so I will show you how I set up the Ground floor button.
- Notice that you don't set an entity for the Button in this instance. Instead, I have added a custom icon, set the Tap Action to Call Service and the Hold Action to No Action. The service I'm going to call is 'input_select: Select'. Remember that we created the Dropdown helper at the beginning and each Picture Elements card is wrapped in a Conditional card that will only display when the input_select is set to that floor. So each button will select a specific floor.
- To set the floor we first need to set the Target to our Floorplan Level dropdown by clicking on the green '+ Pick Entity' button and selecting input_select.floorplan_level. Then set the Option to the correct floor (in this case Ground) and you're done!
Wrap up
OK, I know this was a long post, but hopefully you can see how to set up some pretty cool interactive floorplans now. I've tried to give you examples of enough different elements so you can adapt this to do pretty much anything you can imagine. I still have more that I want to incorporate into my floorplan, such as a switch to toggle between controlling lights and scenes for rooms, but this is a good start.
Of course the big thing I think I ought to do is update the daytime image at different times of the day so that the sun and shadows are correct. This would be fairly easy to do with a set of Conditional elements under the main daytime picture one, but that might be a step too far!
Comments
Post a Comment