Integração Aspirador Xiaomi com Google Assistant e Lovelace

Boas malta,

Venho partilhar a minha experiência de integração do aspirador da Xiaomi (Roborock S5) no Home Assistant.

Introdução

Esta integração tem como objetivos permitir:

  • Mandar limpar uma zona a partir do HA;
  • Configurar várias zonas de uma vez;
  • Suportar o envio de comandos por voz através do Google Assistant em vários idiomas.

A integração com o Google Assistant é feita através do IFTTT (podem seguir este tutorial, mas em vez de canais de televisão são zonas da casa).

Neste tutorial é assumido que o aspirador já está integrado no Home Assistant (neste exemplo a entity_id: vacuum.roborock).

O método e código utilizado funcionam tanto com o firmware original como com o Valetudo e Valetudo RE.

Script Python
Para alcançar os objetivos mencionados acima cria-se um python_script:

"""
LIST OF ROOMS / LISTA DE DIVISÔES

The first position is the name of the room. 
These names target voice assistant, therefore a dictionary is created
to provide more than one name to each room, and thus support 
for different languages
  
The second position is the entity_id name and valetudo zone id
The third column is the room_id in the oficial firmware or Valetudo RE app

When using original firmware:
In order to find the room id one can use trial and error using the following command:
    `miiocli  vacuum --ip <IP> --token <TOKEN> segment_clean <integer number>`
and check the output in the xiaomi app
To run this command install `python-miio`

Array example:
#    Room Name                                       Room code name    Room id
vaccum_room_list = [                                                   
    (['sala', 'living room'],                       'living_room',     [18]),
    (['cozinha', 'kitchen'],                        'kitchen',         [19]),
]

vaccum_room_list = [                                                   
    (['sala', 'living room'],                       'living_room'   ),
    (['cozinha', 'kitchen'],                        'kitchen'       ),
]
"""

application_name = "valetudo_re" # or "xiaomi" or "valetudo"

vaccum_room_list = [                                          
    (['sala', 'living room'],              'LivingRoom',   [16]),
    (['corredor', 'hallway'],              'Hallway',      [17]),
    (['cozinha', 'kitchen'],               'Kitchen',      [18]),
    (['casa de banho', 'bathroom'],        'Bathroom',     [23]),
    (['quarto', 'bedroom'],                'Bedroom',      [21, 22]),     
    (['escritório', 'office'],             'Office',       [20]),      
    (['quarto do fundo', 'guest bedroom'], 'GuestBedroom', [19])
]

# Get vacuum entity_id (if more than one)
entity_id = data.get("entity_id", 'vacuum.roborock')

# This is the room name as per 'room_alias'
room = data.get("room").lower()

# Number of runs per room
runs = int( data.get("runs", '1') )

# Start with delay
delay = int( data.get("delay", '0') )

vaccum_room_param = []
if room == "switch_based":
    # Run through all room that are vacuum friendly
    for r in vaccum_room_list:
        entity_name = ('input_boolean.vacuum_'+r[1]).lower()
        should_vaccum = ( hass.states.get( entity_name ).state == 'on' )
        if should_vaccum:
            if application_name == "xiaomi" or application_name == "valetudo_re":
                vaccum_room_param.extend( r[2] )
            elif application_name == "valetudo":
                vaccum_room_param.append( r[1] )

else:
    # Single room
    for r in vaccum_room_list:
        if room in r[0]:
            if application_name == "xiaomi":
                for i in range( runs ):
                    vaccum_room_param.extend( r[2] )
            elif application_name == "valetudo_re":
                vaccum_room_param.extend( r[2] )
            elif application_name == "valetudo":
                vaccum_room_param.append( r[1] )


if application_name == "xiaomi":
    # Service call when using the original xiaomi app
    service_data = { "entity_id": entity_id, "command": "app_segment_clean", "params": vaccum_room_param } 
elif application_name == "valetudo_re":
    # Service call when using the valetudo app
    service_data = { "entity_id": entity_id, "command": "segmented_cleanup", "params": { 'segment_ids': vaccum_room_param, 'repeats': runs } } 
elif application_name == "valetudo":
    # Service call when using the valetudo app
    service_data = { "entity_id": entity_id, "command": "zoned_cleanup", "params": { 'zone_ids': vaccum_room_param } } 


hass.services.call('vacuum','send_command', service_data, False)

Neste script deve ser configurado a lista de divisões/zones/rooms da vossa casa.

vaccum_room_list = [                                                   
    (['sala', 'living room'],                       'LivingRoom',     [18]),
    (['cozinha', 'kitchen'],                        'Kitchen',         [19]),
]

vaccum_room_list = [                                                   
    (['sala', 'living room'],                       'LivingRoom'   ),
    (['cozinha', 'kitchen'],                        'Kitchen'       ),
]
  1. Array com o nome da divisão (letra minúscula) em vários idiomas. Util para o controlo por voz
  2. Nome de código da divisão/zona tudo pegado. Este é nome usado, por exemplo, no Valetudo.
  3. Array com o ids das divisões na app oficial da Xiaomi ou Valetudo RE (1 ou mais). Caso usem Valetudo não precisam desta coluna.
    Para obter estes ids com firmware original podem usar este comando em tentativa erro com números de 1 a 32 (no Valetudo RE tá la na webapp):
    miiocli vacuum --ip <IP> --token <TOKEN> segment_clean [<id>]

Devem também configurar a app que utilizam: application_name = "valetudo" # or "xiaomi"
Este python_script pode ser chamado no HA usando:

service: python_script.vacuum_room
service_data:
    room: Cozinha
    runs: 1

Nota: O runs aqui é para permitir aspirar várias vezes a mesma divisão (apenas testado com app da Xiaomi).

Inputs para as divisões

Após configurar estas divisões criam um input_boolean para cada uma delas:

input_boolean:
  vacuum_livingroom:
    name: Sala de Estar
    icon: mdi:sofa
  vacuum_kitchen:
    name: Cozinha
    icon: mdi:stove

Notem quem o nome destes input_boolean devem ser vacuum_<codename> sempre em letra minuscula.

Lovelace

Com isto já podemos criar uma view no Lovelace para colocar o nosso aspirador e os input_boolean para selecionar as zonas a aspirar.
Para isso é necessário o card custom:vacuum-card.

- type: custom:vertical-stack-in-card
  cards:
    - type: "custom:vacuum-card"
      actions:
        - icon: "mdi:sofa"
          name: Aspirar Sala
          service: python_script.vacuum_room
          service_data:
            room: Living Room
            runs: 1
        - icon: "mdi:stove"
          name: Aspirar Cozinha
          service: python_script.vacuum_room
          service_data:
            room: Kitchen
            runs: 1
      compact_view: false
      entity: vacuum.roborock
      show_name: true
      show_toolbar: true
      stats:
        cleaning:
          - attribute: currentCleanArea
            subtitle: Área Limpa
            unit: m2
          - attribute: currentCleanTime
            subtitle: Tempo de Limpeza
            unit: minutes
        default:
          - attribute: filter
            subtitle: Filtro
            unit: hours
          - attribute: sideBrush
            subtitle: Escova Lateral
            unit: hours
          - attribute: mainBrush
            subtitle: Escova Principal
            unit: hours
          - attribute: sensor
            subtitle: Sensores
            unit: hours     
    
    - type: conditional
      conditions:
        - entity: sensor.roborock_state
          state: "error"
      card:
        type: markdown
        title: Erro do Aspirador
        content: |
            {{ states.sensor.roborock_state.attributes.Error }}
      
- type: vertical-stack  
  cards:
    - type: entities
      entities:
        - input_boolean.vacuum_livingroom
        - input_boolean.vacuum_kitchen
      state_color: true
      title: Aspirador-Ready (5 máx.)
    - type: horizontal-stack
      cards:
        - aspect_ratio: 12/2
          entity: python_script.vacuum_room
          icon: "mdi:send"
          name: Aspirar Agora
          layout: icon_name_state
          size: 100%
          tap_action:
            action: call-service
            service: python_script.vacuum_room
            service_data:
              room: switch_based
          type: "custom:button-card"

Neste código devem editar as actions e o nome dos input_boolean. Deve existir uma action e um input para cada divisão. Neste exemplo é mostrado apenas duas divisões.

Terá mais ou menos este aspecto no fim (atenção que no meu caso tenho mais divisões):

Usando este código ao clicar “Aspirar Agora” ele vai aspirar as divisões selecionadas pelos input_boolean.

O conditional card é usado para mostrar a mensagem de erro (enviada por MQTT pelo Valetudo) num sensor:

sensor:
  - platform: mqtt
    name: Roborock State
    state_topic: "valetudo/Roborock/state"
    value_template: "{{ value_json.state }}"
    json_attributes_topic: "valetudo/Roborock/state"
    json_attributes_template: "{{ {'Error message': value_json.error } | tojson }}"

Assim sempre que o state for error vai aparecer o texto em markdown por baixo do card do aspirador.

Google Assistant / IFTTT

Por fim falta integrar com o Google Assistant. Para tal devem criar uma routina no IFTTT (seguir este tutorial):

Depois disso criam uma automação para apanhar o comando do IFTTT:

automation old:
  # If You say "Clean $ ", then Make a web request
  # Body configured in IFTTT: { "action": "vacuum_room", "room":"{{TextField}}" }
  - alias: IFTTT Vacuum Room
    trigger:
      - event_data:
          action: vacuum_room
        event_type: ifttt_webhook_received
        platform: event
    action:
      - data_template:
          room: "{{ trigger.event.data.room }}"
        service: python_script.vacuum_room

Atenção ao body do comando no IFTTT:

{ "action": "vacuum_room", "room":"{{TextField}}" }

Esta automação vai chamar o python_script com o nome da divisão (no meu caso em inglês) e aspirar a divisão correspondente.

Código Fonte

Podem consultar todo o YAML que uso no meu Github:

Edit

  1. O cartão do Lovelace deve ser configurado para os atributos correctos, pois varia entre a integração com o miio e o Valetudo. Foi acrescentado um exemplo.
  2. Adicionar conditional card juntamente com um sensor para apanhar a mensagem de erro (testado com Valetudo RE).
  3. Melhorado o suporte para Valetudo RE e adicionei o entity_id (obrigado @codedmind)
4 Likes

Excelente trabalho :star: :star: :star: :star: :star:

Muito bom, espero usar isto quando chegar o meu S5

Obrigado desde já! Enquanto completo Noob no HA, isto vai de certeza ajudar-me imenso :stuck_out_tongue:
Estou a tentar agora configurar algo parecido - só queria mesmo o card com as divisões como mostras, sem IFTTT nem Google Assistant. Deixo apenas uma nota em relação a um problema com que me debati aqui durante uns minutos. Ao correr o segment_clean para descobrir os códigos de divisão, o número entre 1 e 32 tem de ser passado como array, dentro de

Mais logo dou feedback em relação ao resto do processo! O mais certo é bater aqui nalguma parede e não conseguir concluir, mas… crossed fingers :wink:

este comando é para correr onde?

thks

https://python-miio.readthedocs.io/en/latest/discovery.html

Deve-te ajudar :slight_smile:

1 Like

ups… linux… lá terei tirar as teias do meu raspberry

Obg :slight_smile:

Também pode dar no windows ou mesmo através do Home Assistant. Algo onde corras python.

Mas Linux é sempre mais simples.

Obrigado pelo feedback.

Assim certo:
miiocli vacuum --ip <IP> --token <TOKEN> segment_clean [<id>]
Quando fui escrever isto, já não me lembrava do comando e já não tinha o firmware original para testar.

É isso mesmo! Consegui configurar tudo menos o card lateral, onde selecionas as áreas da casa. Mas ainda hei de tentar novamente :stuck_out_tongue:

Bbaixo, eu utilizei Windows. Instalas Python caso não tenhas, e de resto é seguir a doc do miio

1 Like

Já estou a preparar um cartão SD p/ colocar mais logo no meu raspberry.

Depois partilharei o feedback,

O meu robot é um S5 com o firmware original.

Por enquanto não pondero mudar.

Se mudares não aconselho o Valetudo.

Porque vais ficar desiludido com as funções que perdes. E perdes WAF.

Contudo com o Valetudo RE tens tudo o que vez na app e tens controlo sobre o mapa (win-win).

2 Likes

Joâo, qual a grande vantagem do Valetudo RE em relação ao firmware original? É só mesmo pela questão da privacidade/controlo local?

Tens zonas para além das divisões (por exemplo, metade da sala apenas). E tens controlo sobre o mapa (consegues saber as coordenadas dos pontos e guardar locais).

E a privacidade. Mais do que privacidade é ter controlo sobre o que é teu. Até ver estou satisfeito, embora tenha usado durantes uns meses sem problemas o original. Perdi alguns WAF (enganei-me em cima) mas acho que os vou ganhar no long-run.

2 Likes

@Joao_Carreira, o que são WAFs?

Wife Approval Factor :slight_smile:

2 Likes

Bom topico :wink:
Tenho o S50 com firmware original integrado assim:

vacuum:
  - platform: xiaomi_miio
    host: xxxxxx
    token: xxxxxx

O que não disponibiliza o serviço:

Service xiaomi_miio.vacuum_clean_zone

Start the cleaning operation in the areas selected for the number of repeats indicated.

Service data attribute Optional Description
entity_id no Only act on a specific robot
zone no List of zones. Each zone is an array of 4 integer value. Example: [[23510,25311,25110,26361]]
repeats no Number of cleaning repeats for each zone between 1 and 3.

Reparei que usas um serviço “app_segment_clean”. Como fizeste a tua integração para ter este serviço ?

service_data = { "entity_id": entity_id, "command": "app_segment_clean", "params": vaccum_room_param } 

Obrigado :wink:

Olá,

Obrigado :wink:

Tens a versão do firmware com o mapa dividido por divisões? Este tutorial só faz sentido de tiveres essa divisão.

Caso tenhas esse app_segment_clean vai mandar limpar uma dessas “zona/room”.

Sim… fiz upgrade do firmware recentemente da versão que permite definir room’s. E já defini um e fiz restart ao HA… mas não me surge esse serviço para invocar no HA :\

Nem vai aparecer.

O serviço é vacuum.send_command como podes ver aqui:
hass.services.call('vacuum','send_command', service_data, False)

Tudo o resto são opções deste serviço.

Além disso precisas de correr este comando:
miiocli vacuum --ip <IP> --token <TOKEN> segment_clean [<id>]
com diferentes id para saberes quais são os ids das zonas que existem. É tentativa erro.


Copyright © 2017-2020. Todos os direitos reservados
CPHA.pt - info@cpha.pt


FAQ | Termos de Serviço/Regras | Política de Privacidade