Xiaomi Vacuum cleaner Card


#1

Has anyone started to work on a card for Xiaomi Vacuum cleaner, I´m unfortunately a total noob. But would be nice to have a nice looking card for the Xiaomi Vacuum cleaner.


#2

one here
https://sharethelove.io/cards/xiaomi-vacuum-card


#3

Nice thanks :smiley:


#4

Just fix the link

https://sharethelove.io/picture-elements-cards/xiaomi-vacuum-card


#5

Nice work, thx!

I modify a little bit of it. And I integrated my scheduling. That’s how it turned out.


#6

Oh looks cool!
Could you please share config?

I am trying to create something similar except with realtime map from vacuum instead of static image in background.


#7

ui-lovelace.yaml

- icon: mdi:robot-vacuum
    id: vacuum
    title: Robika
    cards:      
      - type: vertical-stack
        cards:
          - type: picture-elements
            image: /local/house/vacuum_card.jpg
            elements:
            - type: icon
              icon: mdi:bell-ring
              tap_action: call-service
              entity: vacuum.xiaomi_vacuum_cleaner
              service: vacuum.locate
              style:
                top: 90%
                left: 65%
                "--paper-item-icon-color": rgb(115, 122, 130)
            - type: icon
              tap_action: call-service
              icon: mdi:home
              entity: vacuum.xiaomi_vacuum_cleaner
              service: vacuum.return_to_base
              style:
                top: 90%
                left: 55%
                "--paper-item-icon-color": rgb(115, 122, 130)
            - type: icon
              icon: mdi:play
              tap_action: call-service
              entity: vacuum.xiaomi_vacuum_cleaner
              service: vacuum.start
              style:
                top: 90%
                left: 45%
                "--paper-item-icon-color": rgb(115, 122, 130)
            - type: icon
              icon: mdi:stop
              tap_action: call-service
              entity: vacuum.xiaomi_vacuum_cleaner
              service: vacuum.stop
              style:
                top: 90%
                left: 35%
                "--paper-item-icon-color": rgb(115, 122, 130)
            - type: state-label
              entity: sensor.vacuum_operation
              style:
                top: 10%
                left: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-color: rgb(34, 154, 210)
                background-color: rgb(54, 65, 78)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_accessories
              style:
                top: 10%
                right: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-color: rgb(34, 154, 210)
                background-color: rgb(54, 65, 78)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_main_brush
              style:
                top: 30%
                right: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-right-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_side_brush
              style:
                top: 45%
                right: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-right-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_filter
              style:
                top: 60%
                right: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-right-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_sensor
              style:
                top: 75%
                right: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-right-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_status
              style:
                top: 30%
                left: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-left-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_battery
              style:
                top: 45%
                left: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-left-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_weekdays
              style:
                top: 75%
                left: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-left-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: state-label
              entity: sensor.vacuum_weekend
              style:
                top: 60%
                left: 1%
                color: rgb(255, 255, 255)
                transform: translate(0%,-50%)
                pointer-events: none
                text-shadow: 1px 1px black
                font-family: Trebuchet MS
                font-size: 90%
                font-weight: bold
                border-left-style: solid
                border-color: rgb(34, 154, 210)
                opacity: 0.8
            - type: image
              entity: automation.takaritas_utemezese_hetkoznap
              tap_action: toggle
              image: /local/house/timer-off.png
              state_image:
                'on': /local/house/timer.png
              state_filter:
                'on': brightness(200%) saturate(1.8)
                'off': brightness(80%) saturate(0.8)
              style:
               top: 75%
               left: 25%
            - type: image
              entity: automation.takaritas_utemezese_hetvegen
              tap_action: toggle
              image: /local/house/timer-off.png
              state_image:
                'on': /local/house/timer.png
              state_filter:
                'on': brightness(200%) saturate(1.8)
                'off': brightness(80%) saturate(0.8)
              style:
               top: 60%
               left: 22%
      - type: conditional
        conditions:
          - entity: automation.takaritas_utemezese_hetkoznap
            state: "on"
        card:
          type: entities
          entities:
            - sensor.vacuum_start_time_weekdays
            - input_number.vacuum_start_hour_weekdays
            - input_number.vacuum_start_minutes_weekdays
      - type: conditional
        conditions:
          - entity: automation.takaritas_utemezese_hetvegen
            state: "on"
        card:
          type: entities
          entities:
            - sensor.vacuum_start_time_weekend
            - input_number.vacuum_start_hour_weekend
            - input_number.vacuum_start_minutes_weekend

sensor.yaml

- platform: template
  sensors:
    vacuum_start_time_weekdays:
      friendly_name: 'Takarítás indítási idő (Hétköznap)'
      value_template: '{{ "%0.02d:%0.02d" | format(states("input_number.vacuum_start_hour_weekdays") | int, states("input_number.vacuum_start_minutes_weekdays") | int) }}'
    vacuum_start_time_weekend:
      friendly_name: 'Takarítás indítási idő (Hétvége)'
      value_template: '{{ "%0.02d:%0.02d" | format(states("input_number.vacuum_start_hour_weekend") | int, states("input_number.vacuum_start_minutes_weekend") | int) }}'
    vacuum_status:
      friendly_name: "Vacuum - Status"
      value_template: "Állapot: {{ states.vacuum.xiaomi_vacuum_cleaner.attributes.status }}"
    vacuum_weekdays:
      friendly_name: "Vacuum - Weekdays"
      value_template: "Hétköznap:"
    vacuum_weekend:
      friendly_name: "Vacuum - Weekend"
      value_template: "Hétvége:"
    vacuum_battery:
      friendly_name: "Vacuum - Battery"
      value_template: "Akku: {{ states.vacuum.xiaomi_vacuum_cleaner.attributes.battery_level }}"
      device_class: battery
      unit_of_measurement: '%'
    vacuum_accessories:
      friendly_name: "Vacuum - Kellékek"
      value_template: "KELLÉKEK ÁLLAPOTA"
    vacuum_operation:
      friendly_name: "Vacuum - Üzemelés"
      value_template: "ÜZEMELÉS"
    vacuum_main_brush:
      friendly_name: "Vacuum - Fő kefe"
      value_template: "Fő kefe: {{ states.vacuum.xiaomi_vacuum_cleaner.attributes.main_brush_left }} h"
    vacuum_side_brush:
      friendly_name: "Vacuum - Első kefe"
      value_template: "Első kefe: {{ states.vacuum.xiaomi_vacuum_cleaner.attributes.side_brush_left }} h"
    vacuum_filter:
      friendly_name: "Vacuum - Filter"
      value_template: "Filter: {{ states.vacuum.xiaomi_vacuum_cleaner.attributes.filter_left }} h"
    vacuum_sensor:
      friendly_name: "Vacuum - Szenzorok"
      value_template: "Szenzorok: {{ states.vacuum.xiaomi_vacuum_cleaner.attributes.sensor_dirty_left }} h"

input_slider.yaml

vacuum_start_minutes_weekend:
  name: Indítási perc
  initial: 0
  min: 0
  max: 59
  step: 1
  mode: slider
vacuum_start_hour_weekend:
  name: Indítási óra
  initial: 13
  min: 0
  max: 23
  step: 1
  mode: slider
vacuum_start_minutes_weekdays:
  name: Indítási perc
  initial: 0
  min: 0
  max: 59
  step: 1
  mode: slider
vacuum_start_hour_weekdays:
  name: Indítási óra
  initial: 15
  min: 0
  max: 23
  step: 1
  mode: slider

automations.yaml

- id: cleaning_timer_weekdays
  alias: Takarítás ütemezése hétköznap
  trigger:
  - platform: template
    value_template: '{{ states.sensor.vacuum_start_time_weekdays.state == states.sensor.time.state
      }}'
  condition:
  - condition: time
    weekday:
    - mon
    - tue
    - wed
    - thu
    - fri
  action:
  - data:
      entity_id: vacuum.xiaomi_vacuum_cleaner
    service: vacuum.start
- id: cleaning_timer_weekend
  alias: Takarítás ütemezése hétvégén
  trigger:
  - platform: template
    value_template: '{{ states.sensor.vacuum_start_time_weekend.state == states.sensor.time.state
      }}'
  condition:
  - condition: time
    weekday:
    - sat
    - sun
  action:
  - data:
      entity_id: vacuum.xiaomi_vacuum_cleaner
    service: vacuum.start

#8

How do you put the real-time image into the background?


#9

Thanks for sharing.

Getting real time map requires rooting of Xiaomi Vaccum. I did that following this tutorial.
And then to get map images I found Valetudo to be the easiest solution.

When that was done on vaccum itself I added HA configuration
Sensor:

- platform: rest
  resource: http://192.168.0.101/api/remote/map
  name: Vacuum Map URL
  value_template: 'http://192.168.0.101{{ value_json.mapsrc }}'

Camera:

  - platform: generic
    name: Vacuum Map
    still_image_url: '{{ states.sensor.vacuum_map_url.state }}'
    verify_ssl: false
    content_type: image/png
    framerate: 1

And you can use that always updating camera in lovelace picture-elements card or any other card that supports camera_image a la

        elements:
          - type: image
            entity: vacuum.servantess
            camera_image: camera.vacuum_map

Hopefully I did not forget something :slight_smile:


#10

Dear Kejszijo, i have try your script work perfectly for basique commande but not for all
i have roborock s50 like you maybe ?

look your script in my home assistant, could you help me please to solve problem ? thank you

homeassistant


#12

Hi,

Yes, I have s50 too. You need a sensor.yaml too, to see left and right informations. You must replace “vacuum.xiaomi_vacuum_cleaner” value to yours in this file, and the Vacuum informations appear.


#13

i have copy your sensor.yaml so, i dont view where is the probleme because is the same file and you and you all information appear

here all information for my vacuum :


#14

Do you see the vacuum sensors like this on https://youraddress/states ?
vacuum%20sensors


#15

No i dont view nothing.

aspi1

but when i click directly in Vaccum card i view all information (configuration.yaml)


#16

My sensor.yaml create many sensor like my picture. So if you can’t see there sensors, the problem is your sensor.yaml. Do you see any errors in the log? If sensors appear, the vacuum informations appear too.


#17

in the log i have this error :

Could not render template Vacuum Status, the state is unknown.

09:10 components/sensor/template.py (WARNING)


#18

and it’s my template.py

> """
> Allows the creation of a sensor that breaks out state_attributes.
> 
> For more details about this platform, please refer to the documentation at
> https://home-assistant.io/components/sensor.template/
> """
> import asyncio
> import logging
> 
> import voluptuous as vol
> 
> from homeassistant.core import callback
> from homeassistant.components.sensor import ENTITY_ID_FORMAT, PLATFORM_SCHEMA
> from homeassistant.const import (
>     ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE,
>     ATTR_ENTITY_ID, CONF_SENSORS)
> from homeassistant.exceptions import TemplateError
> from homeassistant.helpers.entity import Entity, async_generate_entity_id
> from homeassistant.helpers.event import async_track_state_change
> import homeassistant.helpers.config_validation as cv
> 
> _LOGGER = logging.getLogger(__name__)
> 
> SENSOR_SCHEMA = vol.Schema({
>     vol.Required(CONF_VALUE_TEMPLATE): cv.template,
>     vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
>     vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string,
>     vol.Optional(ATTR_ENTITY_ID): cv.entity_ids
> })
> 
> PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
>     vol.Required(CONF_SENSORS): vol.Schema({cv.slug: SENSOR_SCHEMA}),
> })
> 
> 
> @asyncio.coroutine
> # pylint: disable=unused-argument
> def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
>     """Setup the template sensors."""
>     sensors = []
> 
>     for device, device_config in config[CONF_SENSORS].items():
>         state_template = device_config[CONF_VALUE_TEMPLATE]
>         entity_ids = (device_config.get(ATTR_ENTITY_ID) or
>                       state_template.extract_entities())
>         friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
>         unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
> 
>         state_template.hass = hass
> 
>         sensors.append(
>             SensorTemplate(
>                 hass,
>                 device,
>                 friendly_name,
>                 unit_of_measurement,
>                 state_template,
>                 entity_ids)
>             )
>     if not sensors:
>         _LOGGER.error("No sensors added")
>         return False
> 
>     yield from async_add_devices(sensors, True)
>     return True
> 
> 
> class SensorTemplate(Entity):
>     """Representation of a Template Sensor."""
> 
>     def __init__(self, hass, device_id, friendly_name, unit_of_measurement,
>                  state_template, entity_ids):
>         """Initialize the sensor."""
>         self.hass = hass
>         self.entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, device_id,
>                                                   hass=hass)
>         self._name = friendly_name
>         self._unit_of_measurement = unit_of_measurement
>         self._template = state_template
>         self._state = None
> 
>         @callback
>         def template_sensor_state_listener(entity, old_state, new_state):
>             """Called when the target device changes state."""
>             hass.async_add_job(self.async_update_ha_state, True)
> 
>         async_track_state_change(
>             hass, entity_ids, template_sensor_state_listener)
> 
>     @property
>     def name(self):
>         """Return the name of the sensor."""
>         return self._name
> 
>     @property
>     def state(self):
>         """Return the state of the sensor."""
>         return self._state
> 
>     @property
>     def unit_of_measurement(self):
>         """Return the unit_of_measurement of the device."""
>         return self._unit_of_measurement
> 
>     @property
>     def should_poll(self):
>         """No polling needed."""
>         return False
> 
>     @asyncio.coroutine
>     def async_update(self):
>         """Update the state from the template."""
>         try:
>             self._state = self._template.async_render()
>         except TemplateError as ex:
>             if ex.args and ex.args[0].startswith(
>                     "UndefinedError: 'None' has no attribute"):
>                 # Common during HA startup - so just a warning
>                 _LOGGER.warning(ex)
>                 return
>             self._state = None
>             _LOGGER.error(ex)

#19

Which HA version do you use?


#20

The last 0.80.3


#21

@inutilis first thank you for the url sensor, it made all of the so much easier. Not sure if you have solved the live map as background, but I found a solution.
01

You only need two things

shell_command:
  download_new_vacuum_map: "wget -O [full/path/to/hass/config]/www/vacuum_map.jpg {{ states('sensor.vacuum_map_url') }}"

automation:
  - alias: save_new_vacuum_map
    trigger:
      platform: state
      entity_id: sensor.vacuum_map_url
    action:
      - service: shell_command.download_new_vacuum_map

then in ui-lovelace.yaml you just set the picture-elements as:

      - type: picture-elements
        image: /local/vacuum_map.jpg
        elements: