A Smarter Smart Vacuum

This writeup covers the extra features I’ve added to make our smart vacuum feel just a little bit smarter. My custom dashboard makes for really easy targeted cleaning and you can quickly see how long it’s been since each room was cleaned. I also don’t worry about the bin getting full, the vacuum will just go and wait by the bin when it has been cleaning for an hour since it was last emptied.

Custom dashboard alongside the vacuum (with obligatory googly eyes!)
Custom dashboard alongside the vacuum (with obligatory googly eyes!)

The new added features include:

  • A custom dashboard with:
    • Vacuum status + Bin in time
    • Live map
    • Room toggles (including the number of repeats)
      • Each room shows the days since it was last vacuumed
      • The time indicator turns red when rooms surpass a threshold time since cleaning
    • Buttons to clean the selected rooms or summon the vacuum to the bin
  • Auto go-to-bin: If 60mins of cleaning has elapsed since the dustbin was last removed:
    • The vacuum will wait until docked after the current cleaning is complete
    • Then, it will go to the bin if the room it is in is occupied or it will wait until that room is next occupied to go to the bin
    • Once the dustbin is replaced, the bin is considered emptied and the vacuum will return to the dock. (This also works if you press return home on the vacuum or use another override feature)
  • Time since each room was last cleaned:
    • Rooms are automatically selected for the next clean when the time since their last clean has surpassed a custom thresholds. (Just unselect to override)
  • Cast the custom dashboard – The dashboard is displayed on a Google Hub when:
    • Requested via voice command
    • Whenever the vacuum is cleaning
  • A custom vacuum status that includes all the various extra features as well as general vacuum statuses
Example view of the custom dashboard. The current status and bin in time along with a live map are shown on the left. On the right, there are controls to start vacuuming, summon the vacuum to the bin and change the number of repeats. Below this, there are controls to change which rooms will be vacuumed. Each room the time since it was last cleaned. When a room is considered due for cleaning, the room is autoselected after the vacuum has been idle for some time. This makes it easy to start cleaning the overdue rooms quickly.
Example view of the custom dashboard. The left side shows the current status and bin in time, along with a live map. On the top right, there are controls to start vacuuming, summon the vacuum to the bin and change the number of repeats. Below this, there are controls to change which rooms will be vacuumed. Each room shows the time since it was last cleaned. When a room is considered due for cleaning, the room is autoselected after the vacuum has been idle for some time. This makes it easy to start cleaning the overdue rooms quickly.

I’ve tried to make the code (done in NodeRED) as drag and drop as possible. If you’d like to use it yourself, you’ll just need to create a few variables in Home Assistant (detailed below) and modify just a few nodes where you need to set your own desired thresholds for how often rooms should be cleaned. Your vacuum’s entity is autopopulated so you only need to set that once.


  • Home Assistant + NodeRED (addon or otherwise) + (NodeRED websocket)
  • A smart vacuum with Valetudo RE (Note: this is a fork of Valetudo which I haven’t used)
    • If you don’t have a vacuum with Valetudo RE but you can still send commands to your vacuum via some other integration, you could adapt a lot of these flows to suit your needs. Ideally, you should be able to send room-specific vacuum requests and know how long the bin has been in or at least the time of the last cleaning.

Download – You can download a copy of the whole NodeRED flow below:

(note – I used a scrubber program to hopefully make it easier to import the flow. Despite this, when I try to import it, I get an error message on all the Home Assistant nodes which are cleared when I double click on each one. If I figure out how to stop this, I’ll update the file.)

Download – You can download a copy of the YAML code for the whole custom dashboard here:

Smart Vacuums:

If you’re reading this, then I presume you already have a smart vacuum and you’re already well aware of how useful they are. I once read someone describe smart vacuums as tools to keep a clean home clean. I don’t remember where I saw it but it a great description of what they’re best at, cleaning little and often to keep surfaces largely mess free.

Our robot vacuum, a Roborock S50, who we’ve named Sabine (no particular reason) did a great job for over a year with just a basic Home Assistant integration. Then, once I took the plunge and flashed it with Valetudo RE, I could suddenly do so much more with it. I could retrieve its map and cast it to a Google Hub to show how it was getting on. I even set up a notification that requested confirmation before automatically cleaning the whole flat as soon as everyone had left for the day. The pandemic soon put a stop to that notification but that extra time at home gave me a of a chance to implement some more features that made our robot vacuum feel “smarter”. All these extras are really just minor improvements to make using it more convenient, there’s nothing groundbreaking here.

Setup in HomeAssistant:

I’ve gathered the various input selects, numbers, booleans and other parts you should add to your home assistant instance to get this working with your setup. In particular, keeping the naming conventions that I’ve used will mean you won’t have to make changes to the Regex that is used to search through all possible rooms in NodeRED. Generally if you replace “sabine” in all the entity names with whatever your vacuum is called, it should all work.

Input Select: Vacuum status

Create a dropdown helper with at least the following options:

Idle, Bin Needs Emptying, Cleaning, Docked, Returning, Error, Waiting to be Emptied, Just Emptied, Zoned Cleaning

Mine is named input_select.sabine_status

You could add extra options too, these are the minimum required to have these extra features work and to display the basics of what the vacuum is doing.

Sensors: Bin in time

This is a sensor to show the Bin in time for the vacuum. Just change the entity in the value_template line and update the names to suit your vacuum. This sensor works since bin_in_time is one of the attributes displayed by Valetudo RE.

- platform: template
      value_template: "{{ (state_attr('vacuum.sabine', 'bin_in_time') | float / 60 ) | round(1) }}"
      friendly_name: "Sabine Bin In Time"
      unit_of_measurement: "min"

Camera: Vacuum map

If you’d like the live map on the dashboard, try using Valetudo Mapper. I have mine set up to only refresh every 10s (minMillisecondsBetweenMapUpdates: 10000) since it creates a camera entity. It doesn’t seem to use up much CPU this way.

Input Number: Vacuum repeats

This number helper stores how many times each room should be cleaned when a clean command is sent. Mine is named input_number.vacuum_clean_repeats and is constricted between 1 and 3 with a step size of 1.

Input Boolean: Vacuum Automations

To stop the automated sections of this code (like the vacuum going to the bin after an hour of cleaning) create a boolean called input_boolean.vacuum_automations. When this is off those automated parts won’t happen. Since starting the vacuum is not automated (and must be triggered manually) that is not subject to this boolean toggle.

Input Boolean: Vacuum a room

These booleans will store which room will be vacuumed when cleaning is next requested. I use an extra boolean to show if all rooms should be cleaned (this plays nicely with the other booleans). The booleans are called:

input_boolean.vacuum_all_rooms for the All Rooms toggle and input_boolean.living_room_vacuum, input_boolean.hallway_vacuum etc. for the other specific room toggles.

Input Number: Room last vacuumed timestamp

These numbers just store the timestamp of when each room was last cleaned, you’ll need one for each room. I found it easier to use YAML for these but the newer helpers will achieve the same result. The step size should be 1 and for convenience, I’ve set the min/max to be 1/100000000000000000.

Example YAML for the timestamp for the Living Room:

    name: Living Room Last Vacuumed Timestamp
    min: 1
    max: 100000000000000000
    step: 1
    mode: box

Input Number: Days since a room was last vacuumed

Again, you’ll need one of these for each room. They store how long it’s been since each room was last vacuumed. This time the step size is 0.1 since the value will be rounded to this precision.

    name: Living Room Days Since Vacuumed
    min: 0
    max: 100000000000000000
    step: 0.1
    mode: box

Input Text: Days since a room was last vacuumed

This is an alternative to the same info which is stored as a number above. Create input texts for each room of the form: input_text.living_room_time_since_vacuumed etc. Then, a human-readable version of the time since each room was cleaned is also saved. It will read something like ’37 mins ago’ or ‘3 days ago’. I don’t use this on the custom dashboard on the Nest Hub but you may find it useful to display elsewhere.

Input Text: Rooms due for vacuuming

This final variable will just store a list of the rooms that have surpassed their threshold and are now due for cleaning. This list is easy to display among other info on a main dashboard where you don’t want the vacuum info to take up too much space. Create and input text called: input_text.rooms_due_for_vacuuming and the automations will handle the rest.


I use various empty scripts to trigger vacuum automations and events in NodeRED.

For example, I use an empty script called start_sabine_cleaning which when triggered will send the command to the vacuum to clean the selected rooms the requested number of times. I have a similar method for cleaning specific zones rather than rooms. An example of this is the empty start_sabine_cleaning_sofa script.

Other empty scripts called vac_increment_repeats and vac_decrement_repeats change the number of room repeats requested.

I’ve listed an example of the format of these empty scripts below:

    alias: Summon Sabine
    alias: Start Cleaning
    alias: Start Cleaning Sofa
    alias: Vacuum Increment Repeats
    alias: Vacuum Decrement Repeats

Automations in NodeRED:

As with my other writeups, I do all my automation in NodeRED. I find it quicker to debug but also, it’s what I started out learning and I’ve been happy with it so far.

All the code for the vacuum automations is described in more detail here. There are also comments with a similar level of detail near the relevant sections in the code. In particular, I’ve highlighted the few areas where you should make adjustments with a ‘****’ comment in the NodeRED flow. The name of your vacuum is autopopulated so will only need to be set once right at the top of the flow. However some things like scripts to call certain automations are not autopopulated so you should adjust these to suit your configuration. In general I would suggest searching for ‘sabine’ in the code after import and changing it to whatever you vacuum is called or whatever your various scripts have been called.

Many of the trigger nodes use simple regex to match the name of the vacuum or other entities to your specific configuration. This relies on your instance following similar naming trends to what I have described in the Home Assistant section above. Without this you could ammend the various nodes or even give the relevant entity ids explicitly to suit your use case, it shouldn’t be too difficult.

For casting the custom dashboard to a Nest Hub I use CATT along with some further automations that means it won’t interrupt existing media and won’t timeout after 10mins. For casting via voice command, I just use a routine in Google Home to trigger a script that starts casting the custom display temporarily. This is a nice way to check on the vacuum where it will stop casting after 10 minutes if cleaning hasn’t started. This writeup is more about the vacuum so I won’t go further into the casting here.

General Nodes

The first set of nodes set a flow variable (flow.vacuum). You should change this so that flow.vacuum is changed to whatever comes after “vacuum.in the entity id for your vacuum. This value is set on startup to whatever you manually assigned it. Where possible, each flow will use this variable for automations and commands will only be sent to vacuum.{{flow.vacuum}} where {{flow.vacuum}} will be the variable you set here. Some of the scripts that trigger various automations are still set to my explicit examples that include my naming convention. If you are looking to use these flows, just do a search for ‘sabine’ and change the values where they crop up (I’ve tried to minimise this).

Next, there are some nodes that the rest of the automations feed into. These control the custom status of the vacuum and it’s the only place where commands are actually sent to the vacuum. This makes it easy to find all the spots that can cause these things to happen. In these sorts of nodes you’ll often see the use of double curly brackets {{…..}} in various places. In these places those variables are set previously in other areas of the flow. It allows a single node to perform many actions instead of having repeated nodes with slightly altered configurations all over the place.

Below this, the custom status of the vacuum is updated to match the actual status by the vacuum if the custom status is not already set to some special value (like ‘Waiting to be Emptied’).

Increment/Decrement Repeats

This part is triggered by the activation of empty scripts that are triggered when a user presses the plus or minus buttons on the dashboard. The input number, which stores how many times each room should be cleaned, is updated when either option is pressed. Since the limits are set in the input number, the value will always stay between 1 and 3.

Clean Selected Rooms

This is another part of the flow that is triggered by a user pressing something on the dashboard. Once all the rooms to be cleaned are configured and the desired number of repeats has been set, the clean command can be sent to the vacuum. The list of rooms to be cleaned which are sent as part of the ‘send command to vacuum’ node at the start is automatically updated at places later in the flow.

The command to actually start cleaning is only sent to the vacuum after double pressing the start cleaning button on the dashboard. This stops the vacuum being started by accident.

Custom Vacuum Commands

Here’s an example of how you could trigger other custom commands via scripts. The example shows how to start cleaning in a custom zone rather than a room.

Set flow.vacuum_rooms_array

Here, with the use of some regex, a list of all the possible rooms that could be vacuumed (ie. all those input booleans like input_boolean.living_room_vacuum on the dashboard) is returned. Each room, in its lower case form (living_room, not Living Room) is added to the flow.vacuum_rooms_array array. Later, we can loop over the rooms listed in this array if we want to check the something about all the rooms.

Update flow.vacuum_selected_rooms

This part keeps track of which rooms the user would like the vacuum to go to. Any selected room is added to this array and this list is sent to the vacuum. The rooms are transformed here so that they are in the same format as they are listed in Valetudo. eg living_room goes to Living Room and bedroom to Bedroom. You should make sure that your room names in the Valetudo RE web interface match these rooms.

There’s also a bunch of other neat logic here using input_boolean.vacuum_all_rooms. If all the possible rooms are selected then they are all turned off and the all rooms toggle is turned on instead. Whenever the all rooms toggled is turned on, all the rooms are sent to be added to the array of rooms to be vacuumed. If, while the all rooms boolean is on, any of the other off specific room toggles are turned on, the all rooms toggle is turned off and the automation reverts back to joining only the toggled rooms to the array.

Also, if no rooms are selected, the flow variable is deleted so that no rooms can be sent to the vacuum even if cleaning is requested.

That covers the side of automations dealing with how the user interacts with the vacuum. Next, come the various automations that send the vacuum to wait by the bin to be emptied and those that save how long it has been since each room was cleaned.

Smarter dustbin emptying

Valetudo RE very handily released a feature recently that gives a numeric sensor for the time spent cleaning since the dustbin was last removed. This can also be calculated by adding the last clean time to another input number each time cleaning finishes but since the number was right there, it’s much easier to do it that way. If you don’t have this feature on your vacuum, but it does tell you the last clean time, I’ve included a quick bit on how you could do it a bit more manually right at the bottom of all the code. (It’s how I was doing it before the bin time sensor became available).

When the bin in time reaches an hour, I have the custom vacuum status set to “Bin Needs Emptying”. Since I also have a person-specific room presence detection system, (see my other guide for more on that) I didn’t want the vacuum to go wait by the bin unless someone was in the room. So, if the room is occupied when the bin in time triggers, the vacuum goes straight to near the bin. Otherwise, it waits but the status will remain “Bin Needs Emptying”. In either case, once the command to send the vacuum to the bin is issued, the vacuum’s custom status will go to “Waiting to be Emptied”.

Without room presence detection, you can just modify it so the vacuum is sent straight to the bin when it needs emptying.

Then, there are a couple more flows to automate what happens after the dustbin is removed. If the vacuum returns home after the user presses the dock button, this is recognised and the docked status will override the previous waiting to be emptied status. Otherwise, the automation will also recognise the bin out time resetting to 0 and will send the vacuum back to the dock if its current status was “Waiting to be Emptied”. This has been the most satisfying so far. I can just replace the emptied dustbin and the vacuum will automatically return to the dock.

There’s also a third override that does similar things when the custom status is manually changed to “Just Emptied” though I’ve never really used this.

Track which rooms are due for cleaning

This is the latest addition to my vacuum upgrades. This bit of the flow keeps track of the time elapsed since each room was last vacuumed. It gives a really nice visual indicator on the custom dashboard to show which rooms are due for a clean. In addition, there’s an extra bit of automation that will automatically select rooms that pass a threshold. So not only is it really simple to see which rooms are due, but they are automatically selected so you can just send the vacuum to start cleaning and it will go to each room that is due for cleaning.

A room is only marked as cleaned after a clean with that room selected has been going on for at least 30s. This stops a room being marked as clean if vacuuming is started and then suddenly stopped. so

Each room has its own input number for the days since it was last cleaned as discussed in the Home Assistant section. This automation also saves the time since last cleaned in a more human-readable form for each room in an input text variable. A list of all the rooms due to be cleaned is also saved under input_text.rooms_due_for_vacuuming. This can be handy if you’d just like to display a list of the rooms due on a separate dashboard without taking up a bunch of space.

There’s a highlighted function node here where you should add your preferred thresholds for how often a room should be cleaned.

I hope you found this useful, if you’d like to read more of my write ups, check out the list here or feel free to get in touch with any questions.

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top