Domotizar tu lista de la compra 2.0

Domotizar tu lista de la compra 4.0

Hoy vamos a ver cómo domotizar tu lista de la compra para que puedas alimentarla de una forma sencilla e incluir algunas automatizaciones.

Antes de empezar

Si llevas un tiempo con nosotros, es posible que ya hayas usado alguna de las versiones de nuestra lista de la compra. Con cada actualización intento añadir alguna funcionalidad, mejorar la experiencia de instalación y adaptar el código de las tarjetas.

Para evitar errores y acumular «basura» en tu instancia, si quieres aplicar esta versión, te recomiendo que empieces desde cero. No obstante, si eres un experto avanzado, puedes modificar únicamente aquellas partes que te interesen.

Integración de listas

Para domotizar tu lista de la compra vamos a utilizar dos tipos de listas:

  • Una lista principal, que va a ser la lista de referencia a la que acudir para hacer la compra, crear automatizaciones o notificaciones, obtener información de sensores.
  • Listas «pasarela» (tantas como quieras), que vamos a utilizar con el objetivo de añadir objetos a tu lista de la compra desde otros dispositivos (por ejemplo, asistentes de voz).

Elige tu lista principal

Lo primero que tenemos que hacer es elegir cuál queremos que sea nuestra lista de referencia. Lo más intuitivo es pensar que lo lógico es elegir la integración oficial de HA (y así lo hacíamos hasta ahora), pero no tiene por qué.

Por ejemplo, en mi caso llevo años utilizando la aplicación de Bring! Además de gustarme mucho su aplicación, es más fácil de usar para usuarios que se pierden en Home Assistant. Esto la ha convertido hace mucho en la lista de referencia en casa (y de ahí no les saques), por lo que tiene más sentido usarla como lista principal que como una entidad «espejo».

Bring

Esto mismo sería aplicable si, por ejemplo, tú y tu familia sois usuarios del ecosistema de Google, utilizáis los asistentes de voz y la aplicación de listas Google Keep. En este caso tendría sentido usar esa integración como lista principal.

Elijas la opción que elijas, al terminar ya podrás acceder en el menú lateral al apartado «Lista de tareas pendientes» para visualizarla. También puedes localizar la entidad correspondiente en Herramientas para desarrolladores > Estados, cuyo estado es el número de elementos en la lista.

Sincroniza listas pasarela

Dicho lo anterior, en muchas ocasiones he comentado que (personalmente) una de las funciones más útiles que encuentro a domotizar tu lista de la compra es poder añadir productos a la lista con la voz, mientras estoy cocinando o manipulando alimentos (que es cuando me acuerdo).

En mi caso lo hago aprovechando una tablet Android que tengo en la cocina, con el asistente de Google. Más allá de esto, nunca utilizo el asistente de Google (aunque potencialmente podría añadir productos desde el coche o desde mi Android TV).

Para ello solo he tenido que hacer lo siguiente:

  1. Integrar Google Keep, que es donde se almacenan los productos cuando le pides al asistente que apunte algo en tu lista de la compra. No obstante, esto serviría para cualquier otra lista integrable en HA (como, por ejemplo, todoist).
  2. Esto habrá generado una entidad (por ejemplo, ‘todo.lista_de_la_compra_google’), que contiene la lista anterior y la misma estructura que nuestra lista principal.
  1. Ahora voy a crear una sencilla automatización que se va a encargar de detectar cuando he añadido un objeto a la lista de Google, para añadirlo a la lista principal. Para ello utiliza este ‘blueprint’ que he preparado, pulsando en el siguiente botón.
Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.
  1. Luego solo tienes que crear una automatización usando el plano, seleccionar tu lista principal en el primer campo, tu lista pasarela en el segundo y pulsar en «Guardar».
  1. Puedes probar que funciona de una forma sencilla. Desde el menú lateral, accede a «Listas de tareas pendientes» y accede a tu «Lista pasarela». Añade manualmente un objeto, y observa como desaparece de ella y reaparece en tu lista principal.

Esto mismo sucederá cuando añadas un objeto desde tu asistente de voz, o lista complementaria!

Gestiona la lista desde el reloj

Una de las principales ventajas que encuentro a domotizar tu lista de la compra es que, cuando estás en el supermercado, puedes consultarla desde tu smartwatch e ir marcando los productos que has cogido.

Para ello no tienes que hacer nada más, simplemente instalar la aplicación de Home Assistant y añadirla entre las entidades que expones al reloj.

Lista de la compra en Amazfit

Ya sabes que en mi caso utilizo un Amazfit Active 2 NFC, por lo que además puedo añadir productos usando el asistente de voz, añadir mis tarjetas de puntos (Carrefour, LIDL, Ikea, etc.) y pagar directamente con el reloj, por lo que puedo hacer la compra de principio a fin sin sacar el teléfono o la cartera del bolsillo.

Creación de sensores

Creación de sensores de la lista

El siguiente paso para domotizar tu lista de la compra es crear unos sensores que nos permitan extraer los productos de la lista. Esto nos servirá para construir la tarjeta del panel de control y crear automatizaciones.

Vamos a incluir estos sensores dentro del fichero ‘templates.yaml‘ copiando el siguiente código. Recuerda reiniciar Home Assistant para que aparezcan los sensores.

  - trigger:
      - trigger: state
        entity_id: todo.shopping_list
        not_to:
          - unavailable
          - unknown
      - trigger: homeassistant
        event: start
      - trigger: time_pattern
        seconds: /1 
    action:
      - action: todo.get_items
        target:
          entity_id: todo.shopping_list
        data:
          status: needs_action
        response_variable: items_to_buy
    sensor:
      - name: "Shopping list (items to buy)"
        unique_id: shopping_list_uncomplete_items
        state: >
          {{ items_to_buy['todo.shopping_list']['items'] | count() }}
        attributes:
          list: >
            {{ items_to_buy['todo.shopping_list']['items'] | list }}
  - trigger:
      - trigger: state
        entity_id: todo.shopping_list
        not_to:
          - unavailable
          - unknown
      - trigger: homeassistant
        event: start
      - trigger: time_pattern
        minutes: /10  
    action:
      - action: todo.get_items
        target:
          entity_id: todo.shopping_list
        data:
          status: completed
        response_variable: bought_items
    sensor:
      - name: "Shopping list (bought items)"
        unique_id: shopping_list_complete_items
        state: >
          {{ bought_items['todo.shopping_list']['items'] | count() }}
        attributes:
          list: >
            {{ bought_items['todo.shopping_list']['items'] | list }}

Esto creará las siguientes entidades:

  • La entidad ‘sensor.shopping_list_items_to_buy’ tiene como valor el número de productos en la lista de la compra, y el listado en su atributo ‘list’.
  • La entidad ‘sensor.shopping_list_bought_items’ tiene como valor el número de productos comprados recientemente, y el listado en su atributo ‘list’.

Creación del catálogo de productos

Ahora vamos a crear nuestro catálogo de productos, que va a contener aquellos que más compramos y sus características. Esto nos va a permitir crear una interfaz personalizada, automatizaciones avanzadas, notificaciones…

Y lo mejor de todo, es que lo vamos a hacer cómodamente desde una hoja de cálculo de Google Sheets integrada en HA, que se va a encargar de crear y actualizar automáticamente nuestro catálogo.

  1. En Home Assistant, ve a Configuración > Dispositivos y servicios > Añadir integración > Google > Google Sheets, introduce tus credenciales y permite el acceso a tu cuenta de Google. Esto creará una nueva hoja de cálculo en tu cuenta, llamada «Home Assistant».
  1. Renombra la pestaña y dale un título (por ejemplo, «Shopping List»). Luego crea una columna llamada «Product», otra «Category» y, a partir de aquí, tantas columnas como atributos personalizados quieras asignar a los productos (por ejemplo, «Price», «Units», «Quantity», «Store», «Image»…).
  2. Ahora indica en cada fila los datos correspondientes a cada producto, como puedes ver en el ejemplo:
    • Product: el nombre del producto (tal y como lo añades a tu lista).
    • Category: categoría a la que corresponde el producto.
    • Price: precio de referencia del producto.
    • Units: unidades en las que se cobra el producto, en función de si lo compras al peso (‘kg’) o por unidades (‘ud’).
    • Quantity: esta es la cantidad que sueles comprar (por ejemplo, 1 unidad o 0,5 kg).
    • Store: la tienda donde sueles comprar el producto.
    • Image: URL de una imagen o icono que represente el producto.
  1. Comprueba que ya puedes leer los datos de tu hoja desde Configuración > Herramientas para desarrolladores > Acciones > google_sheets.get_sheet. Selecciona tu hoja en el desplegable y pulsa en «Realizar una acción». En unos segundos verás cómo te devuelve la información de los productos.
  1. Ya que estás aquí, haz clic en «Ir al modo YAML» y copia el código que indica el campo ‘config_entry’, ya que lo vas a necesitar a continuación.
  2. Ahora vuelve a tu fichero ‘templates.yaml’, copia el siguiente código y reinicia de nuevo Home Assistant.
  - trigger:
      - trigger: homeassistant
        event: start
      - trigger: event
        event_type: event_template_reloaded
      - trigger: time_pattern
        minutes: /10   
    action:
      - delay: "00:00:30"
      - action: google_sheets.get_sheet
        data:
          config_entry: "23KJN23SFEW24FG224GK" 
          worksheet: "Shopping List"   
          rows: 100
        response_variable: sheet_data
    sensor:
      - name: "Shopping List (catalogue)"
        unique_id: shopping_list_catalogue
        state: "{{ (sheet_data['range'][1:] | length) }}"
        attributes:
          products: >
            {% set ns = namespace(result={}) %}
            {% for row in sheet_data['range'][1:] %}
              {% if row | length > 0 and row[0] != '' %}
                {% set ns.result = ns.result | combine({
                  row[0]: {
                    'category': row[1] if row | length > 1 else '',
                    'price':    row[2] | replace(',', '.') | float(0) if row | length > 2 else 0,
                    'units':    row[3] if row | length > 3 else '',
                    'quantity': row[4] | replace(',', '.') | float(0) if row | length > 4 else 0,
                    'store':    row[5] if row | length > 5 else '',
                    'image':    row[6] if row | length > 6 else ''
                  }
                }) %}
              {% endif %}
            {% endfor %}
            {{ ns.result }}
          last_update: "{{ now().strftime('%d/%m/%Y %H:%M') }}"
  1. Esto habrá creado la entidad ‘sensor.shopping_list_catalogue’, cuyo valor es el número de productos en el catálogo. Además, en sus atributos encontrarás todos los productos y sus características.

Actualización automática del catálogo

Supongo que ahora estarás pensando, «qué pereza añadir uno a uno todos los productos al catálogo». Normal, pero tengo una buena noticia. He creado un ‘blueprint’ que, cada vez que se actualice tu lista de la compra, va a comprobar si hay algún producto nuevo y, en este caso, lo añadirá al catálogo automáticamente.

Esto no solo te ayudará a mantenerlo actualizado, también sirve para realizar una carga inicial con lo que ya tienes en la lista de la compra. A partir de ahí, solo tienes que informar los atributos personalizados que hayas creado para cada producto (categoría, precio, etc.).

Para añadir el ‘blueprint’ sólo tienes que pulsar en el siguiente botón y seleccionar tus entidades. Una vez guardado, haz cualquier cambio en tu lista de la compra y observa como se actualiza automáticamente.

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Y, si tienes n8n instalado en tu ‘homelab‘, también puedes actualizar los precios automáticamente scrapeando la página web de tu supermercado o la API de Mercadona o Alcampo (como dato real, o como referencia).

Diseño del panel de control

Con los sensores que acabamos de crear ya podemos transformar la interfaz para controlar y domotizar tu lista de la compra.

Pero antes…

Ten en cuenta que necesitarás haber instalado a través de HACS las tarjetas de Bubble Card, Card Mod, Auto-Entities y Mushroom Cards. Además, para crear algunas funciones útiles vamos a hacer unas cosas más:

  1. En HA, ve a Configuración > Dispositivos y servicios > Ayudantes > Crear ayudante > Desplegable, llámalo «Shopping List Stores» y añade como opciones las tiendas en las que compras (por ejemplo, «Carrefour», «LIDL», etc.), que ya has indicado en tu catálogo de productos. Esto generará una entidad ‘input_select.shopping_list_stores’, que nos servirá para filtrar productos por tienda.
  2. En HA, ve a Configuración > Dispositivos y servicios > Ayudantes > Crear ayudante > Desplegable, llámalo «Shopping List Filter» y añade como opciones: «Recientes», «Categorías» y «Sugerencias». Esto generará una entidad ‘input_select.shopping_list_filter’, que nos servirá para filtrar productos del catálogo.
  3. En tu fichero ‘templates.yaml’, añade otro nuevo sensor pegando el siguiente código. Esto creará la entidad ‘sensor.shopping_list_next_shopping’, que mostrará el coste aproximado de tu próxima compra (en función de los datos que hayas indicado en tu catálogo).
  - sensor:
    - default_entity_id: sensor.shopping_list_next_shopping
      name: Shopping List (next)
      icon: mdi:cash
      unit_of_measurement: '€'
      state: >
        {% set lista = state_attr('sensor.shopping_list_items_to_buy','list') | map(attribute='summary') | list %}
        {% set productos = state_attr('sensor.shopping_list_items_to_buy','list') | map(attribute='summary') | unique | sort | list %}
        {% set catalogo = state_attr('sensor.shopping_list_catalogue','products') %}
        {% set ns = namespace(total=0.0) %}
        {% for producto in productos -%}
          {%- set unidades = lista | select('equalto', producto) | list | length | float -%}
          {%- set info = catalogo[producto] if catalogo and producto in catalogo else none -%}
          {%- set precio = info.price | float if info else 0.0 -%}
          {%- set cantidad = info.quantity | float(0) if info else 0.0 -%}
          {%- set ns.total = ns.total + (unidades * cantidad * precio) -%}
        {%- endfor %}
        {{ ns.total | round(2) }}
  1. Opcionalmente, en tu hoja de Google Sheets puedes añadir una nueva columna llamada «Sugerencias». La idea es que aquí incluyas un criterio adicional para agrupar tus productos en el catálogo (por ejemplo, «Favoritos», «Dieta», «Disfrutón»…). Además, como es una hoja de cálculo puedes añadir fórmulas (por ejemplo, para que indique «Temporada» solo en unos meses concretos del año). Si usas esta función, necesitarás editar el sensor del catálogo de esta forma, para añadir el atributo.
  - trigger:
      - trigger: homeassistant
        event: start
      - trigger: event
        event_type: event_template_reloaded
      - trigger: time_pattern
        minutes: /30   
    action:
      - delay: "00:00:30"
      - action: google_sheets.get_sheet
        data:
          config_entry: "01KN435KZDSKAJS2CAE1JZMNJY" 
          worksheet: "Shopping List"   
          rows: 100
        response_variable: sheet_data
    sensor:
      - name: "Shopping List (catalogue)"
        unique_id: shopping_list_catalogue
        state: "{{ (sheet_data['range'][1:] | length) }}"
        attributes:
          products: >
            {% set ns = namespace(result={}) %}
            {% for row in sheet_data['range'][1:] %}
              {% if row | length > 0 and row[0] != '' %}
                {% set ns.result = ns.result | combine({
                  row[0]: {
                    'category': row[1] if row | length > 1 else '',
                    'price':    row[2] | replace(',', '.') | float(0) if row | length > 2 else 0,
                    'units':    row[3] if row | length > 3 else '',
                    'quantity': row[4] | replace(',', '.') | float(0) if row | length > 4 else 0,
                    'store':    row[5] if row | length > 5 else '',
                    'image':    row[6] if row | length > 6 else '',
                    'sugerencias':    row[7] if row | length > 7 else ''
                  }
                }) %}
              {% endif %}
            {% endfor %}
            {{ ns.result }}
          last_update: "{{ now().strftime('%d/%m/%Y %H:%M') }}"

Tarjeta de la lista de la compra

Esta es la tarjeta principal, que muestra los productos que están en la lista de la compra.

Como puedes observar, tiene las siguientes características:

  • En la esquina superior derecha encontrarás, primero, el coste aproximado de tu próxima compra. Obviamente, es difícil que coincida al céntimo (especialmente si compras algo al peso).
  • A su lado, un desplegable que te permite filtrar la lista por la tienda asignada a los productos. La idea es que te resulte más fácil hacer la compra en función de dónde te encuentres. Recuerda que el nombre de las tiendas debe coincidir con el que has indicado en el atributo ‘store’ de los sensores de productos.
  • A su derecha encontrarás un acceso directo al catálogo para añadir productos, que veremos a continuación.
  • Debajo, un bloque para cada uno de los productos en la lista de la compra. Si pulsas en dicho bloque, se quitará una unidad de la lista de la compra. Como puedes observar, también se indican las unidades que quieres comprar y, debajo, el precio por unidad/kg. Además, los productos se van a incorporar a tu lista incluso aunque no los hayas incluido en tu catálogo.

Si ya has creado tu catálogo, lo único que tienes que hacer es copiar el siguiente código y se creará automáticamente.

type: vertical-stack
cards:
  - type: custom:bubble-card
    card_type: separator
    name: Lista de la compra
    icon: mdi:shopping
    sub_button:
      main:
        - entity: sensor.shopping_list_next_shopping
          scrolling_effect: false
          show_background: false
          show_icon: false
          show_name: false
          show_state: true
        - name: Tienda
          show_name: false
          show_icon: true
          icon: ""
          entity: input_select.shopping_list_stores
          sub_button_type: select
        - show_name: false
          show_icon: true
          icon: mdi:shopping-search
          tap_action:
            action: navigate
            navigation_path: "#shopping_list"
      bottom: []
    modules:
      - default
    styles: |-
      ha-card {
        margin-top: 10px!important;
        margin-bottom: 10px!important;
      }
      .bubble-name {
        font-size: 15px !important;
      }
  - type: custom:auto-entities
    visibility:
      - condition: state
        entity: sensor.shopping_list_items_to_buy
        state_not: "0"
    show_empty: false
    card:
      square: false
      type: grid
      columns: 3
    card_param: cards
    filter:
      template: >-
        {% set lista = state_attr('sensor.shopping_list_items_to_buy','list') |
        map(attribute='summary') | list %} {% set productos =
        state_attr('sensor.shopping_list_items_to_buy','list') |
        map(attribute='summary') | unique | sort | list %} {% set catalogo =
        state_attr('sensor.shopping_list_catalogue','products') %} {% set tienda
        = states('input_select.shopping_list_stores') %}

        {% for producto in productos -%} {%- set cantidad = lista |
        select('equalto', producto) | list | length | string -%} {%- set info =
        catalogo[producto] if catalogo and producto in catalogo else none -%}
        {%- set picture = info.image if info and info.image else '' -%} {%- set
        precio = info.price | string if info and info.price else '0' -%} {%- set
        unidades = info.units if info else '' -%} {%- set store = info.store if
        info else '' -%}

        {%- if tienda == 'Todas' or tienda == store -%}

          {%- if picture != '' -%}                
              {{
                {
                  'type': 'custom:mushroom-template-card',
                  'entity': 'sensor.shopping_list_items_to_buy',
                  'primary': producto,
                  'secondary': precio + ' €/' + unidades,
                  'badge_text': cantidad,
                  'badge_color': 'black',
                  'picture': picture,
                  'vertical': 'true',
                  'fill_container': 'true',
                  'tap_action': {
                    'action': 'perform-action',
                    'perform_action': 'todo.update_item',
                    'target': { 'entity_id': 'todo.shopping_list' },
                    'data': { 
                      'item': producto,
                      'status': 'completed',
                      },
                  },
                  'card_mod': {
                    'style': {
                      'ha-tile-info$': '.primary { color: #ffffff !important; font-size: 11px !important; } .secondary { color: #ffffff !important; font-size: 11px !important; }',
                      'ha-tile-icon$': '.container { border-radius: 0px !important; } .container img { border-radius: 0px !important; }',
                      '.': 'ha-card { background: linear-gradient(rgba(238, 82, 79, 1), rgba(181, 42, 39, 1)); border-radius: 15px;}'
                    }
                  }   
                }
              }},
          {%- else -%}
              {{
                {
                  'type': 'custom:mushroom-template-card',
                  'entity': 'sensor.shopping_list_items_to_buy',
                  'primary': producto,
                  'badge_text': cantidad,
                  'badge_color': 'black',
                  'icon': 'mdi:shopping',
                  'icon_color': 'white',
                  'vertical': 'true',
                  'fill_container': 'true',
                  'tap_action': {
                    'action': 'perform-action',
                    'perform_action': 'todo.update_item',
                    'target': { 'entity_id': 'todo.shopping_list' },
                    'data': { 
                      'item': producto,
                      'status': 'completed',
                      },
                  },
                  'card_mod': {
                    'style': {
                      'ha-tile-info$': '.primary { color: #ffffff !important; font-size: 11px !important; } .secondary { color: #ffffff !important; font-size: 11px !important; }',
                      'ha-tile-icon$': '.container { border-radius: 0px !important; }',
                      '.': 'ha-card { background: linear-gradient(rgba(238, 82, 79, 1), rgba(181, 42, 39, 1)); border-radius: 15px;} ha-state-icon {--mdc-icon-size: 35px !important;}'
                    }
                  }   
                }
              }},
          {%- endif -%}
        {%- endif -%} {%- endfor %}

Tarjeta para añadir productos

Esta tarjeta sirve para añadir productos a la lista de distintas formas. En mi caso abro esta tarjeta en un popup de Bubble Card, desde el acceso directo que te indicaba en el apartado anterior (aunque la puedes colocar donde quieras).

  • Campo de texto. En la parte superior de la tarjeta siempre aparece un campo de texto que te permite añadir productos a la lista de la compra, sólo con escribirlos. No importa si ya están incluidos o no en tu catálogo.
  • Productos comprados recientemente. En este apartado encontrarás los productos que has comprado recientemente. Si pulsas sobre ellos volverán a la lista de la compra, por lo que es la forma más rápida de hacerla. También puedes limpiar este apartado pulsando sobre el icono «Vaciar» en la parte superior.
  • Productos ordenados por categoría. Desde este apartado podrás encontrar los productos que has introducido en tu catálogo, ordenados alfabéticamente y por categoría (coincidiendo con lo que hayas indicado en los sensores personalizados). Además, sabrás si ya lo has añadido a la lista (en rojo) o no, así como controlar las unidades añadidas desde los iconos correspondientes. Es la mejor forma de hacer la lista más completa.
  • Productos sugeridos. Este bloque funciona igual que el anterior, pero en lugar de clasificar los productos por categoría, lo hace por tipo de sugerencia (siempre que exista). Perfecto, si quieres dejarte guiar a la hora de seleccionar tus productos.
  • Además, en la esquina superior derecha encontrarás un desplegable que te permite saltar de un apartado a otro (como aprendimos con nuestros menús dinámicos).

Si ya has creado tu catálogo, lo único que tienes que hacer es copiar el siguiente código y se creará automáticamente.

type: vertical-stack
cards:
  - type: custom:bubble-card
    card_type: pop-up
    hash: "#shopping_list"
    icon: mdi:shopping-search
    name: Catálogo
    button_type: name
    modules:
      - default
  - type: todo-list
    entity: todo.shopping_list
    hide_completed: true
    hide_create: false
    card_mod:
      style: |
        ha-card {
          background: transparent;
          border: 0px;
          box-shadow: none;  
          margin-left: -10px;
        }
        ha-list {
          display: none;
        }
  - type: vertical-stack
    visibility:
      - condition: state
        entity: input_select.shopping_list_filter
        state: Recientes
    cards:
      - type: custom:bubble-card
        card_type: separator
        name: Recientemente
        icon: mdi:history
        sub_button:
          - name: Vaciar
            show_name: true
            icon: mdi:delete
            tap_action:
              action: perform-action
              perform_action: todo.remove_completed_items
              target:
                entity_id: todo.shopping_list
          - entity: input_select.shopping_list_filter
            name: ""
            show_name: false
            show_icon: true
        modules:
          - default
        card_mod:
          style: |
            ha-card {
              margin-top: 10px!important;
              margin-bottom: 10px!important;
            }
      - type: custom:auto-entities
        visibility:
          - condition: state
            entity: sensor.shopping_list_bought_items
            state_not: "0"
        show_empty: false
        card:
          square: false
          type: grid
          columns: 3
        card_param: cards
        filter:
          template: >-
            {% set productos =
            state_attr('sensor.shopping_list_bought_items','list') |
            map(attribute='summary') | unique | sort | list %} {% set catalogo =
            state_attr('sensor.shopping_list_catalogue','products') %} {% set
            tienda = states('input_select.shopping_list_stores') %}

            {% for producto in productos -%} {%- set info = catalogo[producto]
            if catalogo and producto in catalogo else none -%} {%- set picture =
            info.image if info and info.image else '' -%} {%- set precio =
            info.price | string if info and info.price else '0' -%} {%- set
            unidades = info.units if info else '' -%} {%- set store = info.store
            if info else '' -%}

              {%- if picture != '' -%}                
                  {{
                    {
                      'type': 'custom:mushroom-template-card',
                      'entity': 'sensor.shopping_list_items_to_buy',
                      'primary': producto,
                      'secondary': precio + ' €/' + unidades,
                      'picture': picture,
                      'vertical': 'true',
                      'fill_container': 'true',
                      'tap_action': {
                        'action': 'perform-action',
                        'perform_action': 'todo.update_item',
                        'target': { 'entity_id': 'todo.shopping_list' },
                        'data': { 
                          'item': producto,
                          'status': 'needs_action',
                          },
                      },
                      'card_mod': {
                        'style': {
                          'ha-tile-info$': '.primary { color: #ffffff !important; font-size: 11px !important; } .secondary { color: #ffffff !important; font-size: 11px !important; }',
                          'ha-tile-icon$': '.container { border-radius: 0px !important; } .container img { border-radius: 0px !important; }',
                          '.': 'ha-card { background: linear-gradient( rgba(79, 171, 162, 1), rgba(38, 133, 124, 1) ); border-radius: 15px;}'
                        }
                      }   
                    }
                  }},
              {%- else -%}
                  {{
                    {
                      'type': 'custom:mushroom-template-card',
                      'entity': 'sensor.shopping_list_items_to_buy',
                      'primary': producto,
                      'icon': 'mdi:shopping',
                      'icon_color': 'white',
                      'vertical': 'true',
                      'fill_container': 'true',
                      'tap_action': {
                        'action': 'perform-action',
                        'perform_action': 'todo.update_item',
                        'target': { 'entity_id': 'todo.shopping_list' },
                        'data': { 
                          'item': producto,
                          'status': 'needs_action',
                          },
                      },
                      'card_mod': {
                        'style': {
                          'ha-tile-info$': '.primary { color: #ffffff !important; font-size: 11px !important; } .secondary { color: #ffffff !important; font-size: 11px !important; }',
                          'ha-tile-icon$': '.container { border-radius: 0px !important; }',
                          '.': 'ha-card { background: linear-gradient( rgba(79, 171, 162, 1), rgba(38, 133, 124, 1) ); border-radius: 15px;} ha-state-icon {--mdc-icon-size: 35px !important;}'
                        }
                      }   
                    }
                  }},
              {%- endif -%}
            {%- endfor %}
  - type: vertical-stack
    visibility:
      - condition: state
        entity: input_select.shopping_list_filter
        state: Categorías
    cards:
      - type: custom:bubble-card
        card_type: separator
        name: Categorías
        icon: mdi:view-grid
        sub_button:
          - entity: input_select.shopping_list_filter
            name: ""
            show_name: false
            show_icon: true
        modules:
          - default
        card_mod:
          style: |
            ha-card {
              margin-top: 10px!important;
              margin-bottom: 10px!important;
            }
      - type: custom:auto-entities
        show_empty: false
        card:
          type: vertical-stack
        card_param: cards
        filter:
          template: >-
            {%- set catalogo =
            state_attr('sensor.shopping_list_catalogue','products') %} {%- set
            categories = catalogo.values() | map(attribute='category') | unique
            | sort | list -%} {%- set lista =
            state_attr('sensor.shopping_list_items_to_buy','list') |
            map(attribute='summary') | list %}

            {%- for category in categories -%}
              {%- set category_name = category if category else 'Sin asignar' -%}
              {%- set items_category = catalogo.items() | selectattr('1.category', 'equalto', category) | list -%}
              {%- set size_category = items_category | length -%}

              {{
                {
                  'type': 'custom:bubble-card',
                  'card_type': 'separator',
                  'name': category_name,
                  'card_mod': {
                    'style': 'ha-card { margin-top: 10px!important; margin-bottom: 10px!important; }'
                  },
                  'sub_button': [
                    {
                      'name': size_category | string + ' Productos',
                      'show_name': true,
                      'tap_action': { 'action': 'none' }
                    }
                  ]
                }
              }},

              {%- for nombre, info in items_category -%}
                {%- set to_buy = nombre in lista -%}
                {%- set cantidad = lista | select('equalto', nombre) | list | length -%}
                {%- set gradient = 'rgba(238, 82, 79, 1), rgba(181, 42, 39, 1)' if to_buy else 'rgba(79, 171, 162, 1), rgba(38, 133, 124, 1)' -%}

                {{
                  {
                    'type': 'custom:bubble-card',
                    'card_type': 'button',
                    'button_type': 'state',
                    'show_state': true,
                    'entity': 'sensor.shopping_list_catalogue',
                    'name': nombre + ' (' + cantidad | string + ')' if cantidad | int > 0 else nombre,
                    'icon': 'mdi:shopping',
                    'show_last_changed': false,
                    'tap_action': {
                      'action': 'perform-action',
                      'perform_action': 'todo.add_item',
                      'target': { 'entity_id': 'todo.shopping_list' },
                      'data': { 'item': nombre }
                    },
                    'styles': (
                      ".bubble-icon-container { background-image: url('" + info.image + "') !important; background-size: 25px !important; background-repeat: no-repeat !important; background-color: white !important; background-position: center !important; border-radius: 50% !important; } .bubble-icon-container ha-icon { display: none !important; }"
                      if info.image else ''
                    ),
                    'card_mod': {
                      'style': (
                        '.bubble-state { font-size: 0 !important; ' + ('display: none !important; ' if not info.store else '') + '}'
                        + ' .bubble-state::before { content: "' + (info.store if info.store else '') + '" !important; font-size: 11px !important; color: white !important}'
                        + ' .bubble-state * { display: none !important; }'
                        + ' .bubble-button-background { opacity: 1 !important; background: linear-gradient('
                        + gradient
                        + '); } .bubble-entity-picture { width: 25px !important; height: 25px !important; margin-top: 6px; }'
                      )
                    },
                    'sub_button': [
                      {
                        'name': (
                          ((info.price | string) + ' €/' + (info.units if info.units else '') if info.price else (info.units if info.units else ''))
                        ),
                        'show_name': true,
                        'show_icon': false,
                        'show_background': false
                      },
                      {
                        'icon': 'mdi:plus-thick',
                        'show_background': true,
                        'state_background': false,
                        'tap_action': {
                          'action': 'perform-action',
                          'perform_action': 'todo.add_item',
                          'target': { 'entity_id': 'todo.shopping_list' },
                          'data': { 'item': nombre }
                        }
                      },
                      {
                        'icon': 'mdi:minus-thick',
                        'show_background': true,
                        'state_background': false,
                        'tap_action': {
                          'action': 'perform-action',
                          'perform_action': 'todo.update_item',
                          'target': { 'entity_id': 'todo.shopping_list' },
                          'data': { 'item': nombre, 'status': 'completed' }
                        }
                      }
                    ]
                  }
                }},
              {%- endfor %}
            {%- endfor -%}
  - type: vertical-stack
    visibility:
      - condition: state
        entity: input_select.shopping_list_filter
        state: Sugerencias
    cards:
      - type: custom:bubble-card
        card_type: separator
        name: Sugerencias
        icon: mdi:star
        sub_button:
          - entity: input_select.shopping_list_filter
            name: ""
            show_name: false
            show_icon: true
        modules:
          - default
        card_mod:
          style: |
            ha-card {
              margin-top: 10px!important;
              margin-bottom: 10px!important;
            }
      - type: custom:auto-entities
        show_empty: false
        card:
          type: vertical-stack
        card_param: cards
        filter:
          template: >-
            {%- set catalogo =
            state_attr('sensor.shopping_list_catalogue','products') %} {%- set
            sugerencias = catalogo.values() | map(attribute='sugerencias') |
            reject('equalto', none) | reject('equalto', '') | unique | sort |
            list -%} {%- set lista =
            state_attr('sensor.shopping_list_items_to_buy','list') |
            map(attribute='summary') | list %}

            {%- for sugerencia in sugerencias -%}
              {%- set sugerencia_name = sugerencia if sugerencia else 'Sin asignar' -%}
              {%- set items_sugerencia = catalogo.items() | selectattr('1.sugerencias', 'equalto', sugerencia) | list -%}
              {%- set size_sugerencia = items_sugerencia | length -%}

              {{
                {
                  'type': 'custom:bubble-card',
                  'card_type': 'separator',
                  'name': sugerencia_name,
                  'card_mod': {
                    'style': 'ha-card { margin-top: 10px!important; margin-bottom: 10px!important; }'
                  },
                  'sub_button': [
                    {
                      'name': size_sugerencia | string + ' Productos',
                      'show_name': true,
                      'tap_action': { 'action': 'none' }
                    }
                  ]
                }
              }},

              {%- for nombre, info in items_sugerencia -%}
              {%- if info.sugerencias -%}
                {%- set to_buy = nombre in lista -%}
                {%- set cantidad = lista | select('equalto', nombre) | list | length -%}
                {%- set gradient = 'rgba(238, 82, 79, 1), rgba(181, 42, 39, 1)' if to_buy else 'rgba(79, 171, 162, 1), rgba(38, 133, 124, 1)' -%}

                {{
                  {
                    'type': 'custom:bubble-card',
                    'card_type': 'button',
                    'button_type': 'state',
                    'show_state': true,
                    'entity': 'sensor.shopping_list_catalogue',
                    'name': nombre + ' (' + cantidad | string + ')' if cantidad | int > 0 else nombre,
                    'icon': 'mdi:shopping',
                    'show_last_changed': false,
                    'tap_action': {
                      'action': 'perform-action',
                      'perform_action': 'todo.add_item',
                      'target': { 'entity_id': 'todo.shopping_list' },
                      'data': { 'item': nombre }
                    },
                    'styles': (
                      ".bubble-icon-container { background-image: url('" + info.image + "') !important; background-size: 25px !important; background-repeat: no-repeat !important; background-color: white !important; background-position: center !important; border-radius: 50% !important; } .bubble-icon-container ha-icon { display: none !important; }"
                      if info.image else ''
                    ),
                    'card_mod': {
                      'style': (
                        '.bubble-state { font-size: 0 !important; ' + ('display: none !important; ' if not info.store else '') + '}'
                        + ' .bubble-state::before { content: "' + (info.store if info.store else '') + '" !important; font-size: 11px !important; color: white !important}'
                        + ' .bubble-state * { display: none !important; }'
                        + ' .bubble-button-background { opacity: 1 !important; background: linear-gradient('
                        + gradient
                        + '); } .bubble-entity-picture { width: 25px !important; height: 25px !important; margin-top: 6px; }'
                      )
                    },
                    'sub_button': [
                      {
                        'name': (
                          ((info.price | string) + ' €/' + (info.units if info.units else '') if info.price else (info.units if info.units else ''))
                        ),
                        'show_name': true,
                        'show_icon': false,
                        'show_background': false
                      },
                      {
                        'icon': 'mdi:plus-thick',
                        'show_background': true,
                        'state_background': false,
                        'tap_action': {
                          'action': 'perform-action',
                          'perform_action': 'todo.add_item',
                          'target': { 'entity_id': 'todo.shopping_list' },
                          'data': { 'item': nombre }
                        }
                      },
                      {
                        'icon': 'mdi:minus-thick',
                        'show_background': true,
                        'state_background': false,
                        'tap_action': {
                          'action': 'perform-action',
                          'perform_action': 'todo.update_item',
                          'target': { 'entity_id': 'todo.shopping_list' },
                          'data': { 'item': nombre, 'status': 'completed' }
                        }
                      }
                    ]
                  }
                }},
              {%- endif -%} 
              {%- endfor %}
            {%- endfor -%}

Más ideas para domotizar tu lista de la compra

En el tiempo que llevamos jugando a domotizar tu lista de la compra hemos compartido un montón de ideas para crear automatizaciones, por ejemplo:

  • Añade productos a la lista de forma automática, basándote en la información proporcionada por dispositivos integrados (por ejemplo, añade «Detergente» cada 50 ciclos de la lavadora) o en el periodo de maduración de productos que consumas de forma recurrente (por ejemplo, añade embutido una vez a la semana).
  • Añade productos al escanear etiquetas NFC estratégicamente colocadas por tu casa para facilitarte el trabajo (por ejemplo, en las cajas de la despensa).
  • Añade todos los productos de una receta ejecutando un ‘script’ compuesto por una secuencia de acciones que añade cada uno de sus ingredientes (y, si quieres, crea un libro de recetas en HA).
  • Programa notificaciones cuando alguno de los miembros está en el supermercado, para añadir productos de última hora a la lista.