Información de cualquier web en Home Assistant

Aprende a extraer e incluir información de cualquier web en Home Assistant, cuando no existe ninguna integración que nos proporcione lo que estamos buscando.

Integración del sensor ‘scrape’

Mediante la integración oficial del sensor ‘scrape’ podemos cargar la información de cualquier web en Home Assistant, buscar en el contenido y devolver un valor para utilizarlo. Hay que tener en cuenta que, como indica la documentación de Home Assistant, funciona con páginas web sencillas y puede retrasar la carga.

Para activarlo, accede a Ajustes > Dispositivos y servicios > Añadir integración, busca ‘Scrape‘ y espera a que se inicie el asistente de configuración.

En la primera pantalla tenemos que configurar el recurso, es decir, la página web de la que vas a obtener la información. En el campo “Recurso” tienes que indicar la URL de dicha página. Ten en cuenta que, aunque la información se actualice, la URL de la página debe ser siempre la misma. Para el ejemplo voy a utilizar esta sección de la página Sensacine que muestra las películas en cartelera.

https://www.sensacine.com/peliculas/en-cartelera/cines/

Puesto que se trata de un ejemplo sencillo, vamos a dejar el resto de campos como están y pulsar en “Siguiente“. Esto será suficiente en la mayoría de los casos. No obstante, puedes revisar cada uno de los campos en la documentación oficial.

Identificar el elemento a extraer

Antes de seguir, para incluir información de cualquier web en Home Assistant es bueno que entiendas (al menos de una forma superficial) que las páginas web que visitas están maquetadas con un lenguaje llamado HTML y con hojas de estilo (CSS), entre otros. De esta forma la información de una página web está contenida dentro de capas y elementos.

Sabiendo esto, tenemos que indicarle al ‘scraper’ qué elemento tiene que buscar en el código de la página para que nos devuelva la información contenida dentro del mismo. Afortunadamente podemos identificar el elemento fácilmente sin tener conocimientos de programación.

Si estás utilizando Google Chrome, sólo tienes que marcar la información que quieres extraer y hacer clic con el botón derecho en “Inspeccionar“. En seguida se te abrirá una ventana con el código de la página en cuestión.

Scraper 1

Sobre este código aparecerá sombreada la línea que contiene tu elemento. Sólo tienes que hacer clic derecho sobre esta línea y hacer clic en Copy > Copy selector.

Scraper 2

Creación del sensor

Volvamos al asistente de configuración de la integración ‘scrape’. Cumplimenta el resto de campos de la siguiente forma:

  • Nombre. Aquí va el nombre del sensor que vamos a crear (por ejemplo, “Estrenos 1”).
  • Seleccionar. Aquí tenemos que indicar el elemento específico que tiene que extraer. Por tanto, es aquí donde tienes que copiar el código obtenido tras hacer clic en ‘Copy selector’.

De momento puedes dejar el resto de campos como están. Si reinicias (o recargas) Home Assistant y consultas el sensor que acabas de crear, verás que ya toma el valor de la página web.

Scraper 3

Como en este caso quiero mostrar los últimos 5 estrenos, voy a repetir el proceso con el título correspondiente. Para añadir sensores de la misma página basta con acceder a Ajustes > Dispositivos y servicios > Scrape > Configurar > Añadir sensor. De esta forma ya tengo los siguientes sensores.

Scraper 4

Fijaos que solo con esto ya podríamos hacer cosas útiles. Por ejemplo, en mi caso podría programar una notificación semanal a través de Telegram con los estrenos de la semana.

Integración a través de Multiscrape

Una vez entendido el funcionamiento del sensor ‘scrape’ vamos a hablar del complemento Multiscrape, desarrollado por la comunidad. En esencia este complemento también sirve para incluir información de cualquier web en Home Assistant, con algunas diferencias.

Por un lado, permite establecer la frecuencia con la que extrae la información. Esto es importante, ya que algunas páginas pueden banearte si la frecuencia es demasiado alta (por defecto, la integración oficial lo hace cada 10 minutos). Además vamos a tener algunas opciones más para configurar el sensor.

Para llevar a cabo la integración sigue estos pasos:

  1. Ve a HACS > Integraciones > Explorar y descargar repositorios, busca “Multiscrape“, haz clic en “Descargar” y reinicia Home Assistant.
  2. Accede a tu fichero ‘configuration.yaml’ y añade la siguiente línea
multiscrape: !include multiscrape.yaml
  1. Crea un fichero en la carpeta ‘config’ llamado ‘multiscrape.yaml’. Para ello puedes utilizar el propio complemento editor de texto o con Samba Share. En este fichero vamos a almacenar los sensores que creemos.

A partir de aquí el esquema es muy similar a lo que hacíamos con la integración oficial. Por ejemplo, para crear el sensor de películas en cartelera, he escrito el siguiente código:

- name: Sensacine
  resource: https://www.sensacine.com/peliculas/en-cartelera/cines/
  scan_interval: 86400
  sensor:
    - unique_id: estrenos_1
      name: Estrenos 1
      select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(1) > div > div.meta > h2 > a"

Este ha generado la entidad ‘sensor.estrenos_1’. De la misma forma que antes, hemos indicado la página web de origen de la información bajo el apartado de ‘resource’. Asimismo, hemos indicado en el apartado ‘scan_interval’ la frecuencia de actualización del sensor en segundos (equivalente a 24 horas).

Alimentar los sensores

Ahora vamos a enriquecer la información que contienen los sensores, añadiéndoles atributos. Por ejemplo, en mi caso voy a incorporar el género y la sinopsis. La forma de hacerlo es exactamente la misma que con el sensor principal. Tienes que identificar el elemento que contiene el valor del atributo, y pegarlo en la línea correspondiente.

No obstante, como ves, en este caso la información que busco está “escondida” entre más datos. En concreto me sobra la primera parte, con la fecha de lanzamiento y la duración de la película.

Scraper 5

Por ello voy a crear una ‘template’ para modificar el resultado final del atributo. Lo que voy a hacer es romper (con la función ‘split’) el texto cada vez que encuentre el caracter ‘/’, y le voy a pedir el tercer valor (teniendo en cuenta que empieza a contar por el 0). Además quiero que solo muestre el género principal, por lo que lo voy a volver a romper cada vez que encuentre el caracter ‘,’ y se quede con el primer valor.

Respecto a la sinopsis, no quiero mostrar un texto tan largo. Voy a crear otro ‘template’ para que se quede con los 180 primeros caracteres y luego añada puntos suspensivos. El código final quedaría así:

- name: Sensacine
  resource: https://www.sensacine.com/peliculas/en-cartelera/cines/
  scan_interval: 86400
  sensor:
    - unique_id: estrenos_1
      name: Estrenos 1
      select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(1) > div > div.meta > h2 > a"
      attributes:
        - name: Genero
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(1) > div > div.meta > div > div.meta-body-item.meta-body-info"
          value_template: '{{ ((value.split("/")[2]).split(",")[0]) }}'
        - name: Sinopsis
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(1) > div > div.synopsis > div"
          value_template: '{{ (value)[:180].replace("\n", "")}}...'
    - unique_id: estrenos_2
      name: Estrenos 2
      select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(3) > div > div.meta > h2 > a"
      attributes:
        - name: Genero
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(3) > div > div.meta > div > div.meta-body-item.meta-body-info"
          value_template: '{{ ((value.split("/")[2]).split(",")[0]) }}'
        - name: Sinopsis
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(3) > div > div.synopsis > div"
          value_template: '{{ (value)[:180].replace("\n", "")}}...'
    - unique_id: estrenos_3
      name: Estrenos 3
      select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(7) > div > div.meta > h2 > a"
      attributes:
        - name: Genero
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(7) > div > div.meta > div > div.meta-body-item.meta-body-info"
          value_template: '{{ ((value.split("/")[2]).split(",")[0]) }}'
        - name: Sinopsis
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(7) > div > div.synopsis > div"
          value_template: '{{ (value)[:180].replace("\n", "")}}...'
    - unique_id: estrenos_4
      name: Estrenos 4
      select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(9) > div > div.meta > h2 > a"
      attributes:
        - name: Genero
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(9) > div > div.meta > div > div.meta-body-item.meta-body-info"
          value_template: '{{ ((value.split("/")[2]).split(",")[0]) }}'
        - name: Sinopsis
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(9) > div > div.synopsis > div"
          value_template: '{{ (value)[:180].replace("\n", "")}}...'
    - unique_id: estrenos_5
      name: Estrenos 5
      select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(11) > div > div.meta > h2 > a"
      attributes:
        - name: Genero
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(11) > div > div.meta > div > div.meta-body-item.meta-body-info"
          value_template: '{{ ((value.split("/")[2]).split(",")[0]) }}'
        - name: Sinopsis
          select: "#content-layout > section.section.section-wrap.gd-2-cols.gd-gap-30.row-col-sticky > div > ul > li:nth-child(11) > div > div.synopsis > div"
          value_template: '{{ (value)[:180].replace("\n", "")}}...'

Una vez trasladado el código al resto de sensores, el resultado es el siguiente.

Usos prácticos de los sensores

Como ves podemos extraer información de cualquier web, y crear sensores muy completos. El uso que puedas darle siempre depende de tus necesidades y tu imaginación.

  • Programar notificaciones periódicas por telegram con información actualizada. Por ejemplo, puedes utilizarla para que te envíe los titulares de tu blog favorito, del periódico, estrenos semanales… y en general cualquier fuente de información que consultes de forma recurrente.
  • Crear automatizaciones para que te avise de cambios importantes. Por ejemplo, cuando hay restricciones de acceso a tu ciudad, cuando hay stock del producto que estás esperando, cuando cambia el estado de un proceso que estás vigilando.
  • Crear nuevas tarjetas y funcionalidades. Incorpora esta información a tu panel de control para potenciarlo. Por ejemplo, en mi caso he utilizado la información de los últimos estrenos para crear la siguiente tarjeta. Además de mostrarme la información de los últimos estrenos, si pulso sobre ella me lleva a una búsqueda de la película en Google. De esta forma puedo ver el trailer, buscar información ampliada, o añadirla a mis lista de Google TV.

A continuación te dejo el código para construirla. Para ello necesitas, además de los sensores comentados anteriormente, tener instaladas a través de HACS las tarjetas de Swipe Card, Stack in Card, Card Mod, Mushroom Cards, HTML Card.

type: custom:swipe-card
cards:
  - type: custom:stack-in-card
    card_mod:
      style: |
        ha-card {
          background: url('/local/movies70.jpg');
          background-size: cover;
          padding: 10px;
        }
    cards:
      - type: custom:mushroom-template-card
        primary: '{{ states(''sensor.estrenos_1'')}}'
        secondary: '{{ state_attr(''sensor.estrenos_1'',''genero'')}}'
        icon: mdi:movie-roll
        entity: sensor.estrenos_1
        icon_color: yellow
        tap_action:
          action: none
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -10px;
              margin-left: -10px;
              --card-primary-font-size: 18px;
              --primary-text-color: #ffffff;
              width: 90%;
              }
      - type: custom:html-template-card
        content: >
          <div align="right" style="margin-top:-72px;"> <a
          href="https://www.google.es/search?q={{states('sensor.estrenos_1')}}"
          style="color:#ffeb3b;"><ha-icon
          icon="mdi:information"></ha-icon></a></div>
      - type: custom:mushroom-template-card
        secondary: '{{ state_attr(''sensor.estrenos_1'',''sinopsis'')}}'
        icon: ''
        entity: sensor.estrenos_1
        multiline_secondary: true
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -60px;
              margin-bottom: -10px;
              margin-left: -5px;
              --card-secondary-font-size: 10px;
              --secondary-text-color: #ffffff;
              }
  - type: custom:stack-in-card
    card_mod:
      style: |
        ha-card {
          background: url('/local/movies70.jpg');
          background-size: cover;
          padding: 10px;
        }
    cards:
      - type: custom:mushroom-template-card
        primary: '{{ states(''sensor.estrenos_2'')}}'
        secondary: '{{ state_attr(''sensor.estrenos_2'',''genero'')}}'
        icon: mdi:movie-roll
        entity: sensor.estrenos_2
        icon_color: yellow
        tap_action:
          action: none
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -10px;
              margin-left: -10px;
              --card-primary-font-size: 18px;
              --primary-text-color: #ffffff;
              width: 90%;
              }
      - type: custom:html-template-card
        content: >
          <div align="right" style="margin-top:-72px;"> <a
          href="https://www.google.es/search?q={{states('sensor.estrenos_2')}}"
          style="color:#ffeb3b;"><ha-icon
          icon="mdi:information"></ha-icon></a></div>
      - type: custom:mushroom-template-card
        secondary: '{{ state_attr(''sensor.estrenos_2'',''sinopsis'')}}'
        icon: ''
        entity: sensor.estrenos_2
        multiline_secondary: true
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -60px;
              margin-bottom: -10px;
              margin-left: -5px;
              --card-secondary-font-size: 10px;
              --secondary-text-color: #ffffff;
              }
  - type: custom:stack-in-card
    card_mod:
      style: |
        ha-card {
          background: url('/local/movies70.jpg');
          background-size: cover;
          padding: 10px;
        }
    cards:
      - type: custom:mushroom-template-card
        primary: '{{ states(''sensor.estrenos_3'')}}'
        secondary: '{{ state_attr(''sensor.estrenos_3'',''genero'')}}'
        icon: mdi:movie-roll
        entity: sensor.estrenos_3
        icon_color: yellow
        tap_action:
          action: none
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -10px;
              margin-left: -10px;
              --card-primary-font-size: 18px;
              --primary-text-color: #ffffff;
              width: 90%;
              }
      - type: custom:html-template-card
        content: >
          <div align="right" style="margin-top:-72px;"> <a
          href="https://www.google.es/search?q={{states('sensor.estrenos_3')}}"
          style="color:#ffeb3b;"><ha-icon
          icon="mdi:information"></ha-icon></a></div>
      - type: custom:mushroom-template-card
        secondary: '{{ state_attr(''sensor.estrenos_3'',''sinopsis'')}}'
        icon: ''
        entity: sensor.estrenos_3
        multiline_secondary: true
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -60px;
              margin-bottom: -10px;
              margin-left: -5px;
              --card-secondary-font-size: 10px;
              --secondary-text-color: #ffffff;
              }
  - type: custom:stack-in-card
    card_mod:
      style: |
        ha-card {
          background: url('/local/movies70.jpg');
          background-size: cover;
          padding: 10px;
        }
    cards:
      - type: custom:mushroom-template-card
        primary: '{{ states(''sensor.estrenos_4'')}}'
        secondary: '{{ state_attr(''sensor.estrenos_4'',''genero'')}}'
        icon: mdi:movie-roll
        entity: sensor.estrenos_4
        icon_color: yellow
        tap_action:
          action: none
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -10px;
              margin-left: -10px;
              --card-primary-font-size: 18px;
              --primary-text-color: #ffffff;
              width: 90%;
              }
      - type: custom:html-template-card
        content: >
          <div align="right" style="margin-top:-72px;"> <a
          href="https://www.google.es/search?q={{states('sensor.estrenos_4')}}"
          style="color:#ffeb3b;"><ha-icon
          icon="mdi:information"></ha-icon></a></div>
      - type: custom:mushroom-template-card
        secondary: '{{ state_attr(''sensor.estrenos_4'',''sinopsis'')}}'
        icon: ''
        entity: sensor.estrenos_4
        multiline_secondary: true
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -60px;
              margin-bottom: -10px;
              margin-left: -5px;
              --card-secondary-font-size: 10px;
              --secondary-text-color: #ffffff;
              }
  - type: custom:stack-in-card
    card_mod:
      style: |
        ha-card {
          background: url('/local/movies70.jpg');
          background-size: cover;
          padding: 10px;
        }
    cards:
      - type: custom:mushroom-template-card
        primary: '{{ states(''sensor.estrenos_5'')}}'
        secondary: '{{ state_attr(''sensor.estrenos_5'',''genero'')}}'
        icon: mdi:movie-roll
        entity: sensor.estrenos_5
        icon_color: yellow
        tap_action:
          action: none
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -10px;
              margin-left: -10px;
              --card-primary-font-size: 18px;
              --primary-text-color: #ffffff;
              width: 90%;
              }
      - type: custom:html-template-card
        content: >
          <div align="right" style="margin-top:-72px;"> <a
          href="https://www.google.es/search?q={{states('sensor.estrenos_5')}}"
          style="color:#ffeb3b;"><ha-icon
          icon="mdi:information"></ha-icon></a></div>
      - type: custom:mushroom-template-card
        secondary: '{{ state_attr(''sensor.estrenos_5'',''sinopsis'')}}'
        icon: ''
        entity: sensor.estrenos_5
        multiline_secondary: true
        card_mod:
          style: |
            ha-card {
              background: transparent;
              box-shadow: none;
              margin-top: -60px;
              margin-bottom: -10px;
              margin-left: -5px;
              --card-secondary-font-size: 10px;
              --secondary-text-color: #ffffff;
              }

¿Dudas?¿necesitas ayuda? entra aquí
Y si te ha gustado, compártelo! 🙂
Send this to a friend