Roborock S4 Lovelace Dashboard

11 minute read

Note: this post was written about the Roborock S4 but it should also work with the S5, S5 Max, S6 and S6 Pure.

This is part 3 of a 3 part post on the vacuum.

In this final post I will document the cards I’ve created for my roborock lovelace dashboard. I will briefly go over what each card does, any setup required and finally the lovelace yaml to reproduce the card in your own dashboard.

Vacuum State/Control Card

Vacuum State/Control Card The vacuum state/control card provides some stats about the vacuum, information about it’s state, and a few common controls. The bottom of the card contains the following (from left to right):

  • Current status (clicking opens up more details)
  • Battery Icon based on current state
  • Battery Percent (clicking opens up battery history)
  • Locate Vacuum (clicking will call the vacuum locate service)
  • Return to Dock (clicking will call the return_to_base service)
  • Clean First Floor (clicking will initiate the script to clean the first floor)

Before I detail the yaml necessary to create the card we will first need to create a few custom template sensors to extract some device state attributes that will be added in our picture-elements card.

The yaml below is for Home Assistant < 2021.11.0. If you are using >= 2021.11.0, look at the next section.

- platform: template
  sensors:

    roborock_s4_battery:
      friendly_name: 'Roborock S4 Battery'
      value_template: "{{state_attr('vacuum.roborock_s4', 'battery_level')}}"
      unit_of_measurement: '%'
      device_class: battery

    roborock_s4_lifetime_cleaned_area:
      friendly_name: 'Lifetime Cleaned Area'
      value_template: "{{state_attr('vacuum.roborock_s4', 'total_cleaned_area')}}"
      unit_of_measurement: 

    roborock_s4_lifetime_cleaning_time:
      friendly_name: 'Lifetime Cleaning Time'
      value_template: "{{(state_attr('vacuum.roborock_s4', 'total_cleaning_time') / 60)|round(1, 'floor')}}"

    # NOTE: This date is converted to be timezone aware so that it plays nice
    # with some other templating functions and filters.
    roborock_s4_last_cleaned:
      friendly_name: Relative time since last cleaning ended
      value_template: "{{relative_time(strptime(as_timestamp(state_attr('vacuum.roborock_s4', 'clean_stop'))|timestamp_custom('%Y-%m-%d %H:%M:%S%z'), '%Y-%m-%d %H:%M:%S%z'))}}"

    roborock_s4_lifetime_cleaning_count:
      friendly_name: 'Lifetime Cleaning Count'
      value_template: "{{state_attr('vacuum.roborock_s4', 'cleaning_count')}}"

The yaml below is for Home Assistant >= 2021.11.0. First you will need to enable 3 sensors from the configuration page. sensor.roborock_s4_total_clean_area, sensor.roborock_s4_total_duration and sensor.roborock_s4_total_clean_count

- platform: template
  sensors:
    # Sensors for roborock card.
    roborock_s4_battery:
      friendly_name: "Roborock S4 Battery"
      value_template: "{{state_attr('vacuum.roborock_s4', 'battery_level')}}"
      unit_of_measurement: "%"
      device_class: battery

    roborock_s4_lifetime_cleaned_area:
      friendly_name: "Lifetime Cleaned Area"
      value_template: "{{ states('sensor.roborock_s4_total_clean_area')|round(0, 'floor') }}"
      unit_of_measurement: 

    roborock_s4_lifetime_cleaning_time:
      friendly_name: "Lifetime Cleaning Time"
      entity_id: sensor.roborock_s4_total_duration
      value_template: >
          {% set seconds = states('sensor.roborock_s4_total_duration') %}
          {% set hours = int(seconds) / (60 * 60) %}
          {{ hours|round(1, 'floor') }}

    ## NOTE: This date is converted to be timezone aware in PDT so that it plays nice
    ## with some other templating functions and filters.
    roborock_s4_last_cleaned:
      friendly_name: Last Cleaned Date/Time
      value_template: >
        {{ relative_time(strptime(as_timestamp(states('sensor.roborock_s4_last_clean_end'))|timestamp_custom('%Y-%m-%d %H:%M:%S%z'), '%Y-%m-%d %H:%M:%S%z')) }}

After the above template sensors are added to your configuration.yaml and Home Assistant is restarted you will see these in the States tab in the developer tools.

This card also uses one custom card, lovelace-card-templater. This can be installed manually or via HACS.

type: 'custom:card-templater'
entities:
  - vacuum.roborock_s4
card:
  type: picture-elements
  image: "/local/roborock_s4.jpg"
  elements:
    - type: state-label
      entity: vacuum.roborock_s4
      style:
        left: 0
        right: 0
        bottom: 0
        background-color: "rgba(0, 0, 0, 0.3)"
        padding: 10px
        font-size: 16px
        line-height: 16px
        color: white
        transform: translate(0%,0%)
    - type: icon
      title: Battery
      icon_template: '{{ state_attr("vacuum.roborock_s4", "battery_icon") }}'
      style:
        right: 171px
        bottom: 0
        padding: 10px
        font-size: 16px
        line-height: 16px
        color: white
        transform: translate(0%,0%)
    - type: state-label
      title: Battery Level
      entity: sensor.roborock_s4_battery
      style:
        right: 120px
        bottom: 0px
        padding: 10px
        font-size: 16px
        line-height: 16px
        color: white
        transform: translate(0%,0%)
    - type: icon
      title: Locate Vacuum
      icon: 'mdi:map-marker'
      style:
        right: 90px
        bottom: 0
        padding: 10px
        font-size: 16px
        line-height: 16px
        color: white
        transform: translate(0%,0%)
    - type: icon
      title: Clean First Floor
      icon: 'mdi:broom'
      tap_action:
        action: call-service
        service: mqtt.publish
        service_data:
          payload: 1
          topic: /nodered/vacuum_first_floor
      style:
        right: 0
        bottom: 0
        padding: 10px
        font-size: 16px
        line-height: 16px
        color: white
        transform: translate(0%,0%)
    - type: icon
      title: Return To Base
      icon: 'mdi:home-map-marker'
      tap_action:
        action: call-service
        service: vacuum.return_to_base
        service_data:
          entity_id: vacuum.roborock_s4
      style:
        right: 45px
        bottom: 0
        padding: 10px
        font-size: 16px
        line-height: 16px
        color: white
        transform: translate(0%,0%)
    - type: state-label
      entity: sensor.roborock_s4_last_cleaned
      prefix: 'Last ran '
      suffix: ' ago'
      style:
        background-color: "rgba(0, 0, 0, 0.3)"
        bottom: 85%
        padding: 8px
        font-size: 16px
        line-height: 2px
        color: white
        transform: translate(0%,0%)
    - type: state-label
      prefix: "Ran "
      suffix: "x"
      entity: sensor.roborock_s4_lifetime_cleaning_count
      style:
        background-color: "rgba(0, 0, 0, 0.3)"
        bottom: 85%
        right: 0px
        padding: 8px
        font-size: 16px
        line-height: 2px
        color: white
        transform: translate(0%,0%)
        width: 25%
    - type: state-label
      prefix: "Ran for "
      suffix: " hrs"
      entity: sensor.roborock_s4_lifetime_cleaning_time
      style:
        background-color: "rgba(0, 0, 0, 0.3)"
        bottom: 72%
        right: 0px
        padding: 8px
        font-size: 16px
        line-height: 2px
        color: white
        transform: translate(0%,0%)
        width: 25%
    - type: state-label
      entity: sensor.roborock_s4_lifetime_cleaned_area
      prefix: "Cleaned "
      style:
        background-color: "rgba(0, 0, 0, 0.3)"
        bottom: 59%
        right: 0px
        padding: 8px
        font-size: 16px
        line-height: 2px
        color: white
        transform: translate(0%,0%)
        width: 25%

If you are using Home Assistant >= 2021.11.0 change sensor.roborock_s4_lifetime_cleaning_count to sensor.roborock_s4_total_clean_count.

The asset image for the card can be downloaded here.

Vacuum Room Card

Vacuum Room Card The vacuum room card allows you to specify a particular room along with the number of times to clean it. In order to use this card you will first have to obtain the coordinates for each of your rooms. You can read how I obtained these values in this post, the Home Assistant documentation also contains alternative instructions.

This card also requires 2 input_select’s to be created before it can be used. You will need to add these to your configuration.yaml and restart Home Assistant ( or if you have the latest version of Home Assistant you can call the input_select.reload service).

input_select:
  vacuum_room_select:
    name: Choose a room to clean
    options:
      - Bathroom
      - Dining Room
      - Guest Bedroom
      - Hallway
      - Kitchen
      - Living Room

  vacuum_room_repeat:
    name: Number of times to clean the room
    options:
      - 1
      - 2
      - 3
    initial: 1
    icon: mdi:numeric

This card also uses one custom card, lovelace-card-templater. This can be installed manually or via HACS.

type: "custom:card-templater"
card:
  type: entities
  title: Vacuum a Room
  show_header_toggle: false
  entities:
    - input_select.vacuum_room_select
    - input_select.vacuum_room_repeat
    - type: call-service
      service: mqtt.publish
      service_data:
        topic: /nodered/vacuum_room
        payload_template: >-
          {
            "room": "{{ states.input_select.vacuum_room_select.state }}",
            "repeat": {{ states.input_select.vacuum_room_repeat.state }}
          }
      name: " "
      icon: " "
entities:
  - input_select.vacuum_room_select
  - input_select.vacuum_room_repeat

When the Run button is clicked it will publish an mqtt message to the /nodered/vacuum_room topic. This will be read by the Vacuum Room Node-RED flow, which will properly create the parameters and make the service call.

Maintenance Cards

Maintenance Cards The maintenance cards highlight the part of the vacuum, how many hours are left until it needs to be cleaned or replaced, and provides a way to reset the counter after you’ve done the maintenance.

Before we can use any of these cards we will need to create 2 custom template sensors for each card. The first will create a sensor for the percentage remaining and the second will create a sensor for the number of hours remaining.

The yaml below is for Home Assistant < 2021.11.0. If you are using >= 2021.11.0, look at the next section.

- platform: template
  sensors:

    # Used for the Filter maintenance card
    roborock_s4_filter_remaining:
      friendly_name: '% Filter Remaining'
      unit_of_measurement: '%'
      value_template: "{{((state_attr('vacuum.roborock_s4', 'filter_left') / 150) * 100) | round | int}}"

    # Used for the Filter maintenance card
    roborock_s4_filter_hrs_remaining:
      friendly_name: 'Filter Remaining Hours'
      unit_of_measurement: 'hrs'
      value_template: "{{state_attr('vacuum.roborock_s4', 'filter_left')}}"

    # Used for the Side Brush maintenance card
    roborock_s4_side_brush_remaining:
      friendly_name: '% Side Brush Remaining'
      unit_of_measurement: '%'
      value_template: "{{((state_attr('vacuum.roborock_s4', 'side_brush_left') / 200) * 100) | round | int}}"

    # Used for the Side Brush maintenance card
    roborock_s4_side_brush_hrs_remaining:
      friendly_name: 'Side Brush Remaining Hours'
      unit_of_measurement: 'hrs'
      value_template: "{{state_attr('vacuum.roborock_s4', 'side_brush_left')}}"

    # Used for the Main Brush maintenance card
    roborock_s4_main_brush_remaining:
      friendly_name: '% Main Brush Remaining'
      unit_of_measurement: '%'
      value_template: "{{((state_attr('vacuum.roborock_s4', 'main_brush_left') / 300) * 100) | round | int}}"

    # Used for the Main Brush maintenance card
    roborock_s4_main_brush_hrs_remaining:
      friendly_name: 'Main Brush Remaining Hours'
      unit_of_measurement: 'hrs'
      value_template: "{{state_attr('vacuum.roborock_s4', 'main_brush_left')}}"

    # Used for the Sensors maintenance card
    roborock_s4_sensors_remaining:
      friendly_name: '% Sensors Remaining'
      unit_of_measurement: '%'
      value_template: "{{((state_attr('vacuum.roborock_s4', 'sensor_dirty_left') / 30) * 100) | round | int}}"

    # Used for the Sensors maintenance card
    roborock_s4_sensors_hrs_remaining:
      friendly_name: 'Sensors Remaining Hours'
      unit_of_measurement: 'hrs'
      value_template: "{{state_attr('vacuum.roborock_s4', 'sensor_dirty_left')}}"

The yaml below is for Home Assistant >= 2021.11.0. First you will need to enable 4 sensors from the configuration page. sensor.roborock_s4_filter_left, sensor.roborock_s4_side_brush_left, sensor.roborock_s4_sensor_dirty_left and sensor.roborock_s4_main_brush_left.

- platform: template
  sensors:
    # Used for the Filter maintenance card
    roborock_s4_filter_remaining:
      friendly_name: "% Filter Remaining"
      unit_of_measurement: "%"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_filter_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ ((hours / 150) * 100) | round | int }}

    # Used for the Filter maintenance card
    roborock_s4_filter_hrs_remaining:
      friendly_name: "Filter Remaining Hours"
      unit_of_measurement: "hrs"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_filter_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ hours | round | int }}

    # Used for the Side Brush maintenance card
    roborock_s4_side_brush_remaining:
      friendly_name: "% Side Brush Remaining"
      unit_of_measurement: "%"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_side_brush_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ ((hours / 200) * 100) | round | int }}

    # Used for the Side Brush maintenance card
    roborock_s4_side_brush_hrs_remaining:
      friendly_name: "Side Brush Remaining Hours"
      unit_of_measurement: "hrs"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_side_brush_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ hours | round | int }}

    # Used for the Main Brush maintenance card
    roborock_s4_main_brush_remaining:
      friendly_name: "% Main Brush Remaining"
      unit_of_measurement: "%"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_main_brush_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ ((hours / 300) * 100) | round | int }}

    # Used for the Main Brush maintenance card
    roborock_s4_main_brush_hrs_remaining:
      friendly_name: "Main Brush Remaining Hours"
      unit_of_measurement: "hrs"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_main_brush_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ hours | round | int }}

    # Used for the Sensors maintenance card
    roborock_s4_sensors_remaining:
      friendly_name: "% Sensors Remaining"
      unit_of_measurement: "%"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_sensor_dirty_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ ((hours / 30) * 100) | round | int }}

    # Used for the Sensors maintenance card
    roborock_s4_sensors_hrs_remaining:
      friendly_name: "Sensors Remaining Hours"
      unit_of_measurement: "hrs"
      value_template: >
        {% set seconds = int(states('sensor.roborock_s4_sensor_dirty_left')) %}
        {% set hours = seconds / (60 * 60) %}
        {{ hours | round | int }}

After these are added to your configuration.yaml and you have restarted Home Assistant you can then add the following picture-elements cards to your lovelace configuration.

- type: horizontal-stack
  cards:
    - type: picture-elements
      image: /local/side_brush.png
      elements:
        - type: state-label
          entity: sensor.roborock_s4_side_brush_remaining
          title: "% Remaining Until Side Brush Should Be Replaced"
          style:
            font-size: 30px
            color: orange
            left: 0px
            right: 0px
            bottom: 0px
            background-color: "rgba(0, 0, 0, 0.3)"
            transform: translate(0%,0%)
        - type: state-label
          title: Hours Remaining Until Side Brush Should Be Replaced
          entity: sensor.roborock_s4_side_brush_hrs_remaining
          suffix: " left"
          style:
            right: 0px
            bottom: 0px
            padding: 10px
            font-size: 16px
            line-height: 16px
            color: white
            transform: translate(0%,0%)
        - type: icon
          icon: mdi:restart
          title: Reset Hours
          tap_action:
            action: call-service
            service: vacuum.send_command
            service_data:
              entity_id: vacuum.roborock_s4
              command: reset_consumable
              params: ["side_brush_work_time"]
            confirmation:
              text: Are you sure you want to reset the hours remaining counter for replacing the side brush?
          style:
            top: 0px
            right: 0px
            padding: 7px
            transform: translate(0%,0%)
    - type: picture-elements
      image: /local/sensors.png
      elements:
        - type: state-label
          entity: sensor.roborock_s4_sensors_remaining
          title: "% Remaining Until Sensors Should Be Cleaned"
          style:
            font-size: 30px
            color: orange
            left: 0px
            right: 0px
            bottom: 0px
            background-color: "rgba(0, 0, 0, 0.3)"
            transform: translate(0%,0%)
        - type: state-label
          title: "Hours Remaining Until Sensors Should Be Cleaned"
          entity: sensor.roborock_s4_sensors_hrs_remaining
          suffix: " left"
          style:
            right: 0px
            bottom: 0px
            padding: 10px
            font-size: 16px
            line-height: 16px
            color: white
            transform: translate(0%,0%)
        - type: icon
          icon: mdi:restart
          title: Reset Hours
          tap_action:
            action: call-service
            service: vacuum.send_command
            service_data:
              entity_id: vacuum.roborock_s4
              command: reset_consumable
              params: ["sensor_dirty_time"]
            confirmation:
              text: Are you sure you want to reset the hours remaining counter for cleaning the sensors?
          style:
            top: 0px
            right: 0px
            padding: 7px
            transform: translate(0%,0%)

- type: horizontal-stack
  cards:
    - type: picture-elements
      image: /local/filter.png
      elements:
        - type: state-label
          title: "% Remaining Until Filter Should Be Replaced"
          entity: sensor.roborock_s4_filter_remaining
          style:
            font-size: 30px
            color: orange
            left: 0px
            right: 0px
            bottom: 0px
            background-color: "rgba(0, 0, 0, 0.3)"
            transform: translate(0%,0%)
        - type: state-label
          title: "Hours Remaining Until Filter Should Be Replaced"
          entity: sensor.roborock_s4_filter_hrs_remaining
          suffix: " left"
          style:
            right: 0px
            bottom: 0px
            padding: 10px
            font-size: 16px
            line-height: 16px
            color: white
            transform: translate(0%,0%)
        - type: icon
          icon: mdi:restart
          title: Reset Hours
          tap_action:
            action: call-service
            service: vacuum.send_command
            service_data:
              entity_id: vacuum.roborock_s4
              command: reset_consumable
              params: ["filter_work_time"]
            confirmation:
              text: Are you sure you want to reset the hours remaining counter for replacing the filter?
          style:
            top: 0px
            right: 0px
            padding: 7px
            transform: translate(0%,0%)
    - type: picture-elements
      image: /local/main_brush.png
      elements:
        - type: state-label
          title: "% Remaining Until Main Brush Should Be Replaced"
          entity: sensor.roborock_s4_main_brush_remaining
          style:
            font-size: 30px
            color: orange
            left: 0px
            right: 0px
            bottom: 0px
            background-color: "rgba(0, 0, 0, 0.3)"
            transform: translate(0%,0%)
        - type: state-label
          title: "Hours Remaining Until Main Brush Should Be Replaced"
          entity: sensor.roborock_s4_main_brush_hrs_remaining
          suffix: " left"
          style:
            right: 0px
            bottom: 0px
            padding: 10px
            font-size: 16px
            line-height: 16px
            color: white
            transform: translate(0%,0%)
        - type: icon
          icon: mdi:restart
          title: Reset Hours
          tap_action:
            action: call-service
            service: vacuum.send_command
            service_data:
              entity_id: vacuum.roborock_s4
              command: reset_consumable
              params: ["main_brush_work_time"]
            confirmation:
              text: Are you sure you want to reset the hours remaining counter for replacing the main brush?
          style:
            top: 0px
            right: 0px
            padding: 7px
            transform: translate(0%,0%)

Clicking on the reset icon will pop up a confirmation alert that you will have to click on to continue with the action. This was added to prevent an accidental click on the card from resetting the counter.

The assets for the above cards can be found below: