Criando sensores com o componente MultiScrape! Sensores de Futebol e Formula 1!

O conteúdo desse tutorial foi criado pelo @Leo e pelo @jhbcosta , só estou divulgando, qualquer dúvida podem deixar abaixo aqui no tutorial.

Para criarmos os sensores vamos precisar do componente MultiScrape, então vamos instala-lo!

Vá no HACS e clique em Integrações!

Clique em Explore e ADD Repositories no canto direito inferior. Na lupa pesquise por “multi”, clique no MultiScrape.

Na tela do componente clique em Instalar Esse Repositório No HACS.

Clique em Instalar.

Com ele instalado agora é só reiniciar o Home Assistant.

7

Com o Home Assistant reiniciado agora temos que criar os sensores. Vá até o configuration.yaml do seu Home Assistant para adicionar os códigos como no exemplo abaixo.

Futebol

Para o Flamengo:

multiscrape:
  - resource: https://www.flamengo.com.br/home
    scan_interval: 60
    sensor:
      - unique_id: flamengo_proximojogo_vs
        name: Próximo Jogo
        select: ".team-vs"
      - unique_id: flamengo_proximojogo_campeonato
        name: Campeonato
        select: ".championship"
      - unique_id: flamengo_proximojogo_datahora
        name: Data do Jogo
        select: ".event-time"

Para criar um cartão do Flamengo:

- type: entities
            entities:
              - entity: sensor.flamengo_proximojogo_vs
                icon: 'mdi:soccer'
              - entity: sensor.flamengo_proximojogo_campeonato
                icon: 'mdi:trophy'
              - entity: sensor.flamengo_proximojogo_datahora
                icon: 'mdi:calendar'
            title: Futebol

9

Para outros times é necessário mudar o endereço em “resource” coloque o link do seu time nesse site:

https://www.placardefutebol.com.br/time/sao-paulo.html

Para outros times:

- platform: multiscrape
  name: Tricolor
  resource: https://www.placardefutebol.com.br/time/sao-paulo.html
  scan_interval: 30
  selectors:
    tricolor_proximojogo_local:
      name: Time Local
      select: "#livescore > div:nth-child(2) > a:nth-child(1) > div.row.align-items-center.content > div:nth-child(2) > h5"
      value_template: "{{ value | upper() }}"
    tricolor_proximojogo_visitante:
      name: Time Visitante
      select: "#livescore > div:nth-child(2) > a:nth-child(1) > div.row.align-items-center.content > div:nth-child(6) > h5"
      value_template: "{{ value | upper() }}"
    tricolor_proximojogo_data:
      name: Data do Jogo
      select: "#livescore > div:nth-child(2) > a:nth-child(1) > div.row.align-items-center.content > div.w-25.p-1.status.text-center > span"
      value_template: "{{ value | upper() }}"
    tricolor_proximojogo_campeonato:
      name: Campeonato
      select: "div > a:nth-child(1) > div.match-card-league-name"
      value_template: "{{ value | upper() }}"

Para criar um cartão:

type: entities
entities:
  - entity: sensor.tricolor_proximojogo_local
    icon: 'mdi:soccer'
  - entity: sensor.tricolor_proximojogo_visitante
    icon: 'mdi:soccer'
  - entity: sensor.tricolor_proximojogo_campeonato
    icon: 'mdi:trophy'
  - entity: sensor.tricolor_proximojogo_data
    icon: 'mdi:calendar'

Pode ser feito com qualquer outro time:

photo_2021-05-15_13-15-51

Formula 1

- platform: multiscrape
  name: Fórmula 1
  resource: https://motorsport.uol.com.br/category/f1/schedule/upcoming/
  scan_interval: 30
  selectors:
    f1_proxima:
      name: Próxima Corrida
      select: "#app_event_schedule-browse-by-category > div.root > div.ms-page-content.ms-page-content--two-columns > div.ms-content.ms-content--with-sidebar > div.ms-content_main > div.ms-schedule-month-results.ms-schedule-fullwidth.ms-mb > div > table > tbody.ms-schedule-table__item.ms-schedule-table__item--upcoming.ms-schedule-table__item--open > tr.ms-schedule-table__item-main > td.ms-schedule-table__cell.ms-schedule-table__cell--date > div"
      value_template: "{{ value | replace('*', '') | upper() }}"
    f1_local:
      name: Local
      select: "#app_event_schedule-browse-by-category > div.root > div.ms-page-content.ms-page-content--two-columns > div.ms-content.ms-content--with-sidebar > div.ms-content_main > div.ms-schedule-month-results.ms-schedule-fullwidth.ms-mb > div > table > tbody.ms-schedule-table__item.ms-schedule-table__item--upcoming.ms-schedule-table__item--open > tr.ms-schedule-table__item-main > td.ms-schedule-table__cell.ms-schedule-table__cell--main > div > div.ms-schedule-table-item-main__start > div.ms-schedule-table-item-main__info > div.ms-schedule-table-item-main__event"
      value_template: "{{ value | upper() }}"

Para o cartão:

type: 'entities'
entities:
  - entity: sensor.f1_proxima
    icon: 'mdi:calendar'
  - entity: sensor.f1_local
    icon: 'mdi:map-marker'

photo_2021-05-15_11-54-40

Agradecimentos ao @Leo e ao @jhbcosta por terem criado, divulgado e ajudado com o tutorial.

Um abraço.

8 Likes

Belo tutorial. Mais um pra sua conta! Parabéns!

Só não entendi como personalizar os “selectors”. Talvez seja a parte mais difícil, mas que, quando entendida, permite obter qualquer informação da web…

Excelente… estou batendo cabeça tem um tempo aqui… rsrs… mas nao consegui desenrolar… até entendi a lógica dos selectors, mas não estou conseguindo reproduzir para o meu time, São Paulo… seria bacana se o pessoal que já for conseguindo fazer, ir colocando os selectors aqui, para facilitar… Abraços!

Tutorial atualizado com novas informações cedidas pelo @jhbcosta !

1 Like

Olá Leandro,
Tem dois pequenos erros na criação do card do São Paulo, segue o correto…

type: entities
entities:
  - entity: sensor.tricolor_proximojogo_local
    icon: 'mdi:soccer'
  - entity: sensor.tricolor_proximojogo_visitante
    icon: 'mdi:soccer'
  - entity: sensor.tricolor_proximojogo_campeonato
    icon: 'mdi:trophy'
  - entity: sensor.tricolor_proximojogo_data
    icon: 'mdi:calendar'

Post Show de Bola!!!

PS - Não consigo colocar o espaçamento correto, e o bullet está saindo como um ponto, não como traço que é o correto…

1 Like

Obrigado por ter avisado, já alterei lá em cima!

1 Like

Obrigado por incluir o campeonato de F1, nem todos acompanha o futebol.

1 Like

Códigos atualizados!

*** Mais uma atualização 29/05/2021***
Agora o card da corrida é dinâmico, os eventos não aparecem mais a media que vão acontecendo. Só ficam visíveis os eventos futuros do fim de semana de corida.
Adicionado um card com a classificação atual do mundial de pilotos e de construtores.

Com a devida licença, fiz um aprimoramento para a Formula 1. Pegando todos os detalhes do fim de semana de corrida.

Fiz todo o procedimento pelo node-red, não precisa do componente do HACS multiscrape. Fica assim no Lavelace.

image

No dia da corrida, por exemplo, o card vai estar assim, não exibindo o que já passou. Cada evento é ocultado 2hs depois de iniciado.

image

O fluxo no node-red, fica assim:

[{"id":"19099ba2.b1b6a4","type":"tab","label":"Formula 1","disabled":false,"info":""},{"id":"fba17ac0.b784a8","type":"group","z":"19099ba2.b1b6a4","name":"Formula 1","style":{"stroke":"#ff0000","fill":"#ffbfbf","label":true},"nodes":["aa7f8e92.03bda","a96a85f1.bf4838","c0df2d87.d36f5","6e65e010.32934","ff318ea3.16f74","35cdf149.fde0ee","376241fc.05547e","e1eacfc6.a9118","1b85099b.0054a6","1a32d3c1.a09e7c","bd1aa579.9de548","8ea1e653.84e4b8","e2bfefa2.26aaf","b3c405c0.00b0e8","5ed0ee1f.f7883","440e5952.f17a78","8fa3952b.6aba98","5af05323.6104fc","78d7dd42.69a214","82d6deaf.880d5","29b7b7a8.43dae8"],"x":74,"y":59,"w":912,"h":542},{"id":"aa7f8e92.03bda","type":"inject","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"A cada 30 min","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"*/30 0-23 * * *","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":100,"wires":[["a96a85f1.bf4838","440e5952.f17a78"]]},{"id":"a96a85f1.bf4838","type":"http request","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"F1 Mania","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://www.f1mania.net/f1-formula-1-calendario-2021/","tls":"","persist":false,"proxy":"","authType":"basic","x":380,"y":100,"wires":[["c0df2d87.d36f5"]]},{"id":"c0df2d87.d36f5","type":"html","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Obtem Calendário","property":"payload","outproperty":"payload","tag":"body.page-template-default.page.page-id-194938 > main > div.container > div > div.entry-post > div.the-content","ret":"text","as":"multi","x":550,"y":100,"wires":[["6e65e010.32934"]]},{"id":"6e65e010.32934","type":"split","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Separa os GPs","splt":">> Voltar ao topo","spltType":"str","arraySplt":"1","arraySpltType":"len","stream":false,"addname":"","x":760,"y":100,"wires":[["ff318ea3.16f74"]]},{"id":"ff318ea3.16f74","type":"join","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Monta Array","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"1","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":750,"y":160,"wires":[["35cdf149.fde0ee"]]},{"id":"35cdf149.fde0ee","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Formata Gps","func":"var dadosGPs = msg.payload.splice(1,msg.payload.length-2);\n\n//var dadosGPs = msg.payload.splice(15,msg.payload.length-17);\n\n\nmsg = {};\nmsg.payload = {};\nmsg.payload.gps = dadosGPs\n\n\nmsg = {};\nmsg.payload = {};\nmsg.payload.gps = [];\nvar treino = \"\";\n\nfor (i=0; i<dadosGPs.length; i++)\n{\n    \n    if ((dadosGPs[i].split(\"\\n\")).length == 13)\n    {\n        // Local\n        msg.payload.gps[i] = {};\n        msg.payload.gps[i].local = (dadosGPs[i].split(\"\\n\")[3]);\n        \n        // Treino 1\n        stringData = (dadosGPs[i].split(\"\\n\")[4]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[5]).split(\" \")[0];\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].treino1 = new Date();\n        msg.payload.gps[i].treino1.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].treino1.setHours(hora);\n        msg.payload.gps[i].treino1.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].treino1.setMinutes(minutos);\n        }\n        msg.payload.gps[i].treino1.setSeconds(0);\n        msg.payload.gps[i].treino1.setMilliseconds(0);\n        \n        // Treino 2\n        stringData = (dadosGPs[i].split(\"\\n\")[4]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[6]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].treino2 = new Date();\n        msg.payload.gps[i].treino2.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].treino2.setHours(hora);\n        msg.payload.gps[i].treino2.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].treino2.setMinutes(minutos);\n        }\n        msg.payload.gps[i].treino2.setSeconds(0);\n        msg.payload.gps[i].treino2.setMilliseconds(0);\n        \n        // Treino 3\n        stringData = (dadosGPs[i].split(\"\\n\")[7]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[8]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].treino3 = new Date();\n        msg.payload.gps[i].treino3.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].treino3.setHours(hora);\n        msg.payload.gps[i].treino3.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].treino3.setMinutes(minutos);\n        }\n        msg.payload.gps[i].treino3.setSeconds(0);\n        msg.payload.gps[i].treino3.setMilliseconds(0);\n        \n        // Qualificação\n        stringData = (dadosGPs[i].split(\"\\n\")[7]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[9]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].qualificacao = new Date();\n        msg.payload.gps[i].qualificacao.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].qualificacao.setHours(hora);\n        msg.payload.gps[i].qualificacao.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].qualificacao.setMinutes(minutos);\n        }\n        msg.payload.gps[i].qualificacao.setSeconds(0);\n        msg.payload.gps[i].qualificacao.setMilliseconds(0);\n        \n        // Corrida\n        stringData = (dadosGPs[i].split(\"\\n\")[10]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[11]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].corrida = new Date();\n        msg.payload.gps[i].corrida.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].corrida.setHours(hora);\n        msg.payload.gps[i].corrida.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].corrida.setMinutes(minutos);\n        }\n        msg.payload.gps[i].corrida.setSeconds(0);\n        msg.payload.gps[i].corrida.setMilliseconds(0);\n    }\n    else\n    {   \n        // Local\n        msg.payload.gps[i] = {};\n        msg.payload.gps[i].local = (dadosGPs[i].split(\"\\n\")[3]);\n        \n        // Treino 1\n        stringData = (dadosGPs[i].split(\"\\n\")[4]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[5]).split(\" \")[0];\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].treino1 = new Date();\n        msg.payload.gps[i].treino1.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].treino1.setHours(hora);\n        msg.payload.gps[i].treino1.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].treino1.setMinutes(minutos);\n        }\n        msg.payload.gps[i].treino1.setSeconds(0);\n        msg.payload.gps[i].treino1.setMilliseconds(0);\n        \n        // Treino 2\n        stringData = (dadosGPs[i].split(\"\\n\")[6]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[7]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].treino2 = new Date();\n        msg.payload.gps[i].treino2.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].treino2.setHours(hora);\n        msg.payload.gps[i].treino2.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].treino2.setMinutes(minutos);\n        }\n        msg.payload.gps[i].treino2.setSeconds(0);\n        msg.payload.gps[i].treino2.setMilliseconds(0);\n        \n        // Treino 3\n        stringData = (dadosGPs[i].split(\"\\n\")[8]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[9]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].treino3 = new Date();\n        msg.payload.gps[i].treino3.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].treino3.setHours(hora);\n        msg.payload.gps[i].treino3.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].treino3.setMinutes(minutos);\n        }\n        msg.payload.gps[i].treino3.setSeconds(0);\n        msg.payload.gps[i].treino3.setMilliseconds(0);\n        \n         // Qualificação\n        stringData = (dadosGPs[i].split(\"\\n\")[8]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[10]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].qualificacao = new Date();\n        msg.payload.gps[i].qualificacao.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].qualificacao.setHours(hora);\n        msg.payload.gps[i].qualificacao.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].qualificacao.setMinutes(minutos);\n        }\n        msg.payload.gps[i].qualificacao.setSeconds(0);\n        msg.payload.gps[i].qualificacao.setMilliseconds(0);\n        \n        \n        // Corrida\n        stringData = (dadosGPs[i].split(\"\\n\")[11]).split(\" – \")[1] + (dadosGPs[i].split(\"\\n\")[12]).split(\" \")[0]\n        ano = stringData.split(\"/\")[2].split(\":\")[0];\n        mes = stringData.split(\"/\")[1];\n        dia = stringData.split(\"/\")[0];\n        hora = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[0];\n        minutos = stringData.split(\"/\")[2].split(\":\")[1].split(\"h\")[1];\n        msg.payload.gps[i].corrida = new Date();\n        msg.payload.gps[i].corrida.setFullYear(ano, mes-1, dia);\n        msg.payload.gps[i].corrida.setHours(hora);\n        msg.payload.gps[i].corrida.setMinutes(0);\n        if(minutos !== '')\n        {\n            msg.payload.gps[i].corrida.setMinutes(minutos);\n        }\n        msg.payload.gps[i].corrida.setSeconds(0);\n        msg.payload.gps[i].corrida.setMilliseconds(0);\n\n    }\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":230,"y":220,"wires":[["376241fc.05547e"]]},{"id":"376241fc.05547e","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Próxima Corrida","func":"// Horário atual\nvar dateNow = new Date();\n\n// Debug\n//dateNow = new Date(2021,6,17,10,1,0)\n\n// Considera 2h antes, para só passar para o próximo evento 2hs depois de iniciado\ndateTimeCheck = new Date();\ndateTimeCheck.setTime(dateNow.getTime() - (2*60*60*1000))\n\n// Separa as corridas\nnumGPs = msg.payload.gps.length;\n\n// Prucura a próxima corrida\nfor (i=0; i<numGPs; i++)\n{\n   if (msg.payload.gps[i].corrida < dateTimeCheck)\n   {\n        continue;\n   }\n   else\n   {\n       tr = '1'\n       GP = msg.payload.gps[i]\n       msg = {};\n       msg.payload = {};\n       msg.payload = GP;\n       msg.dateNow = dateNow;\n\n       return msg;\n   }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":220,"wires":[["1b85099b.0054a6","1a32d3c1.a09e7c"]]},{"id":"e1eacfc6.a9118","type":"mqtt out","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"MQTT Formula 1","topic":"","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"5404109e.eedc6","x":870,"y":220,"wires":[]},{"id":"1b85099b.0054a6","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Separa Eventos","func":"// Horário atual\nvar dateNow = msg.dateNow;\n\n// Soma 2hs, momento que atualiza para o próximo evento\ndatePularEvento = new Date();\ndatePularEvento.setTime(dateNow.getTime() - (2*60*60*1000))\n\n// Local\nmsg1 = {}\nmsg1 = {\n    topic:\"formula1/evento/local\",\n    payload: msg.payload.local\n}\nnode.send(msg1);\n\n// Treino 1\nmsg2 = {}\nd = msg.payload.treino1\ntime =  d.getDate() + \"/\" +  ('0' + (d.getMonth()+1)).slice(-2) +  \"    \" + ('0' + (d.getHours())).slice(-2) + \":\" + ('0' + (d.getMinutes())).slice(-2) + \"h\";\nmsg2 = {\n    topic:\"formula1/evento/treino1\",\n    payload: time\n}\nnode.send(msg2);\nif (datePularEvento > msg.payload.treino1)\n{\n    msg2 = \n    {\n        topic:\"formula1/evento/treino1/availability\",\n        payload: 'offline'\n    }\n}\nelse\n{\n    msg2 = \n    {\n        topic:\"formula1/evento/treino1/availability\",\n        payload: 'online'\n    }\n}\nnode.send(msg2);\n\n// Treino 2\nmsg3 = {}\nd = msg.payload.treino2\ntime =   d.getDate() + \"/\" +  ('0' + (d.getMonth()+1)).slice(-2) +  \"    \" + ('0' + (d.getHours())).slice(-2) + \":\" + ('0' + (d.getMinutes())).slice(-2) + \"h\";\nmsg3 = {\n    topic:\"formula1/evento/treino2\",\n    payload: time\n}\nnode.send(msg3);\nif (datePularEvento > msg.payload.treino2)\n{\n    msg3 = \n    {\n        topic:\"formula1/evento/treino2/availability\",\n        payload: 'offline'\n    }\n}\nelse\n{\n    msg3 = \n    {\n        topic:\"formula1/evento/treino2/availability\",\n        payload: 'online'\n    }\n}\nnode.send(msg3);\n\n// Treino 3\nmsg4 = {}\nd = msg.payload.treino3\ntime =  d.getDate() + \"/\" +  ('0' + (d.getMonth()+1)).slice(-2) +  \"    \" + ('0' + (d.getHours())).slice(-2) + \":\" + ('0' + (d.getMinutes())).slice(-2) + \"h\";\nmsg4 = {\n    topic:\"formula1/evento/treino3\",\n    payload: time\n}\nnode.send(msg4);\nif (datePularEvento > msg.payload.treino3)\n{\n    msg4 = \n    {\n        topic:\"formula1/evento/treino3/availability\",\n        payload: 'offline'\n    }\n}\nelse\n{\n    msg4 = \n    {\n        topic:\"formula1/evento/treino3/availability\",\n        payload: 'online'\n    }\n}\nnode.send(msg4);\n\n// Qualificação\nmsg5 = {}\nd = msg.payload.qualificacao\ntime =  d.getDate() + \"/\" +  ('0' + (d.getMonth()+1)).slice(-2) +  \"    \" + ('0' + (d.getHours())).slice(-2) + \":\" + ('0' + (d.getMinutes())).slice(-2) + \"h\";\nmsg5 = {\n    topic:\"formula1/evento/qualificacao\",\n    payload: time\n}\nnode.send(msg5);\nif (datePularEvento > msg.payload.qualificacao)\n{\n    msg5 = \n    {\n        topic:\"formula1/evento/qualificacao/availability\",\n        payload: 'offline'\n    }\n}\nelse\n{\n    msg5 = \n    {\n        topic:\"formula1/evento/qualificacao/availability\",\n        payload: 'online'\n    }\n}\nnode.send(msg5);\n\n// Corrida\nmsg6 = {}\nd = msg.payload.corrida\ntime =  d.getDate() + \"/\" +  ('0' + (d.getMonth()+1)).slice(-2) +  \"    \" + ('0' + (d.getHours())).slice(-2) + \":\" + ('0' + (d.getMinutes())).slice(-2) + \"h\";\nmsg6 = {\n    topic:\"formula1/evento/corrida\",\n    payload: time\n}\nnode.send(msg6);\nif (datePularEvento > msg.payload.corrida)\n{\n    msg6 = \n    {\n        topic:\"formula1/evento/corrida/availability\",\n        payload: 'offline'\n    }\n}\nelse\n{\n    msg6 = \n    {\n        topic:\"formula1/evento/corrida/availability\",\n        payload: 'online'\n    }\n}\nnode.send(msg6);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":220,"wires":[["e1eacfc6.a9118"]]},{"id":"1a32d3c1.a09e7c","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Ajusta Alarme","func":"// Offset Aviso Treino 1 [min]\noffsetTreino1 = 10;\n\n// Offset Aviso Treino 2 [min]\noffsetTreino2 = 10;\n\n// Offset Aviso Treino 3 [min]\noffsetTreino3 = 10;\n\n// Offset Aviso Qualificação [min]\noffsetQualificacao = 30;\n\n// Offset Aviso Corrida [min]\noffsetCorrida1 = 60;\noffsetCorrida2 = 10;\n\nvar dateNow = new Date();\nvar time;\n\n// Antes do Treino 1\nif (dateNow < msg.payload.treino1)\n{\n    messagem = \"Faltam \" + offsetTreino1 + \" min para o Treino 1 do \" + msg.payload.local;\n    flow.set('avisoF1',messagem);\n    \n    time = msg.payload.treino1;\n    time.setTime(time.getTime() - (offsetTreino1*60*1000))\n\n    msg = {};\n    msg.time = time.getFullYear() + \"-\" + ('0' + (time.getMonth()+1)).slice(-2) + \"-\" + time.getDate() + \" \" + ('0' + (time.getHours())).slice(-2) + \":\" + ('0' + (time.getMinutes())).slice(-2) + \":\" + ('0' + (time.getSeconds())).slice(-2);\n    msg.messagem = messagem;\n    return msg;\n}\n\n// Antes do Treino 2\nelse if (dateNow < msg.payload.treino2)\n{\n    messagem = \"Faltam \" + offsetTreino2 + \" min para o Treino 2 do \" + msg.payload.local;\n    flow.set('avisoF1',messagem);\n    \n    time = msg.payload.treino2;\n    time.setTime(time.getTime() - (offsetTreino2*60*1000))\n\n    msg = {};\n    msg.time = time.getFullYear() + \"-\" + ('0' + (time.getMonth()+1)).slice(-2) + \"-\" + time.getDate() + \" \" + ('0' + (time.getHours())).slice(-2) + \":\" + ('0' + (time.getMinutes())).slice(-2) + \":\" + ('0' + (time.getSeconds())).slice(-2);\n    msg.messagem = messagem;\n    return msg;\n}\n\n// Antes do Treino 3\nelse if (dateNow < msg.payload.treino3)\n{\n    messagem = \"Faltam \" + offsetTreino3 + \" min para o Treino 3 do \" + msg.payload.local;\n    flow.set('avisoF1',messagem);\n    \n    time = msg.payload.treino3;\n    time.setTime(time.getTime() - (offsetTreino3*60*1000))\n\n    msg = {};\n    msg.time = time.getFullYear() + \"-\" + ('0' + (time.getMonth()+1)).slice(-2) + \"-\" + time.getDate() + \" \" + ('0' + (time.getHours())).slice(-2) + \":\" + ('0' + (time.getMinutes())).slice(-2) + \":\" + ('0' + (time.getSeconds())).slice(-2);\n    msg.messagem = messagem;\n    return msg;\n}\n\n// Qualificação\nelse if (dateNow < msg.payload.qualificacao)\n{\n    messagem = \"Faltam \" + offsetQualificacao + \" min para a Qualificação do \" + msg.payload.local;\n    flow.set('avisoF1',messagem);\n    \n    time = msg.payload.qualificacao;\n    time.setTime(time.getTime() - (offsetQualificacao*60*1000))\n\n    msg = {};\n    msg.time = time.getFullYear() + \"-\" + ('0' + (time.getMonth()+1)).slice(-2) + \"-\" + time.getDate() + \" \" + ('0' + (time.getHours())).slice(-2) + \":\" + ('0' + (time.getMinutes())).slice(-2) + \":\" + ('0' + (time.getSeconds())).slice(-2);\n    msg.messagem = messagem;\n    return msg;\n}\n\n// Corrida Aviso 1\nelse if (dateNow < msg.payload.corrida)\n{\n    messagem = \"Faltam \" + offsetCorrida1 + \" min para a Corrida do \" + msg.payload.local;\n    flow.set('avisoF1',messagem);\n    \n    time = msg.payload.corrida;\n    time.setTime(time.getTime() - (offsetCorrida1*60*1000))\n\n    msg = {};\n    msg.time = time.getFullYear() + \"-\" + ('0' + (time.getMonth()+1)).slice(-2) + \"-\" + time.getDate() + \" \" + ('0' + (time.getHours())).slice(-2) + \":\" + ('0' + (time.getMinutes())).slice(-2) + \":\" + ('0' + (time.getSeconds())).slice(-2);\n    msg.messagem = messagem;\n    return msg;\n}\n\n// Corrida Aviso 2\nelse if (dateNow < msg.payload.corrida)\n{\n    messagem = \"Faltam \" + offsetCorrida2 + \" min para a Corrida do \" + msg.payload.local;\n    flow.set('avisoF1',messagem);\n    \n    time = msg.payload.corrida;\n    time.setTime(time.getTime() - (offsetCorrida2*60*1000))\n\n    msg = {};\n    msg.time = time.getFullYear() + \"-\" + ('0' + (time.getMonth()+1)).slice(-2) + \"-\" + time.getDate() + \" \" + ('0' + (time.getHours())).slice(-2) + \":\" + ('0' + (time.getMinutes())).slice(-2) + \":\" + ('0' + (time.getSeconds())).slice(-2);\n    msg.messagem = messagem;\n    return msg;\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":260,"wires":[["bd1aa579.9de548"]]},{"id":"bd1aa579.9de548","type":"api-call-service","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Próximo Evento","server":"fa7437b1.7faae8","version":3,"debugenabled":false,"service_domain":"input_datetime","service":"set_datetime","entityId":"input_datetime.proximo_evento","data":"{\"entity_id\":\"input_datetime.proximo_evento\",\"datetime\":\"{{time}}\"}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":860,"y":260,"wires":[[]]},{"id":"8ea1e653.84e4b8","type":"ha-time","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Avisa Evento","server":"fa7437b1.7faae8","version":0,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityId":"input_datetime.proximo_evento","property":"","offset":"0","offsetType":"num","offsetUnits":"seconds","randomOffset":false,"repeatDaily":false,"payload":"$entity().state","payloadType":"jsonata","debugenabled":true,"x":170,"y":560,"wires":[["e2bfefa2.26aaf"]]},{"id":"e2bfefa2.26aaf","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"","func":"msg = {};\nmsg.payload = {}\nmsg.payload = flow.get(\"avisoF1\");\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":560,"wires":[["b3c405c0.00b0e8"]]},{"id":"b3c405c0.00b0e8","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Mensagem Telegram","func":"var mensagem = msg.payload;\n\nmsg = {};\nmsg.payload = {};\nmsg.payload.destinatario = \"Antonio\"\nmsg.payload.type = 'message'\nmsg.payload.content = \"🏎🏎 \" + mensagem + \" 🏎🏎\"\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":560,"wires":[["5ed0ee1f.f7883"]]},{"id":"5ed0ee1f.f7883","type":"link out","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Para Telegram","links":["a180195c.57b218"],"x":775,"y":560,"wires":[]},{"id":"440e5952.f17a78","type":"http request","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"F1 Mania","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://www.f1mania.net/f1-formula-1-classificacao-2021/","tls":"","persist":false,"proxy":"","authType":"basic","x":400,"y":340,"wires":[["8fa3952b.6aba98"]]},{"id":"8fa3952b.6aba98","type":"html","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Obtem Classificação","property":"payload","outproperty":"payload","tag":"body.page-template-default.page.page-id-212907 > main > div.container > div > div.entry-post > div.the-content > p","ret":"text","as":"multi","x":600,"y":340,"wires":[["5af05323.6104fc"]]},{"id":"5af05323.6104fc","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Pilotos / Construtores","func":"var numItens = msg.payload.split('\\n');\n\nif (numItens.length > 15)\n{\n    return [msg, null];\n}\nif (numItens.length > 5)\n{\n    return [null, msg];\n}","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":440,"wires":[["82d6deaf.880d5"],["78d7dd42.69a214"]]},{"id":"82d6deaf.880d5","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Divide","func":"// Local\nvar pilotos = msg.payload.split('\\n');\nmsg1 = {};\n\n// Divide em grupos de 8, pois o sensor não aceita muitos carracters\nvar mensagem = pilotos[0];\nfor (i=1; i<8; i++)\n{\n    mensagem = mensagem + '\\r' + pilotos[i];\n}\nmsg1 = {\n    topic:\"formula1/mundial_pilotos/1\",\n    payload: mensagem\n}\nnode.send(msg1);\n\nmensagem = pilotos[8];\nfor (i = 9; i<16; i++)\n{\n    mensagem = mensagem + '\\r' + pilotos[i];\n}\nmsg1 = {\n    topic:\"formula1/mundial_pilotos/2\",\n    payload: mensagem\n}\nnode.send(msg1);\n\nmensagem = pilotos[16];\nfor (i = 17; i<pilotos.length; i++)\n{\n    mensagem = mensagem + '\\r' + pilotos[i];\n}\nmsg1 = {\n    topic:\"formula1/mundial_pilotos/3\",\n    payload: mensagem\n}\nnode.send(msg1);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":420,"wires":[["29b7b7a8.43dae8"]]},{"id":"78d7dd42.69a214","type":"function","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"Divide","func":"// Local\nvar contrutores = msg.payload.split('\\n');\nmsg1 = {};\n\n// Divide em grupos de 8, pois o sensor não aceita muitos carracters\nvar mensagem = contrutores[0];\nfor (i=1; i<8; i++)\n{\n    mensagem = mensagem + '\\r' + contrutores[i];\n}\nmsg1 = {\n    topic:\"formula1/mundial_construtores/1\",\n    payload: mensagem\n}\nnode.send(msg1);\n\nmensagem = contrutores[8];\nfor (i = 9; i<contrutores.length; i++)\n{\n    mensagem = mensagem + '\\r' + contrutores[i];\n}\nmsg1 = {\n    topic:\"formula1/mundial_construtores/2\",\n    payload: mensagem\n}\nnode.send(msg1);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":460,"wires":[["29b7b7a8.43dae8"]]},{"id":"29b7b7a8.43dae8","type":"mqtt out","z":"19099ba2.b1b6a4","g":"fba17ac0.b784a8","name":"MQTT Formula 1","topic":"","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"5404109e.eedc6","x":710,"y":440,"wires":[]},{"id":"5404109e.eedc6","type":"mqtt-broker","name":"Mosquitto","broker":"192.168.68.200","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"fa7437b1.7faae8","type":"server","name":"Home Assistant","version":1,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

Para sensores agora devemos usar:

- platform: mqtt
  state_topic: 'formula1/evento/local'
  name: 'Local'
  icon: mdi:flag-checkered

- platform: mqtt
  state_topic: 'formula1/evento/treino1'
  name: 'Treino 1'
  icon: mdi:calendar-clock
  availability:
    - topic: "formula1/evento/treino1/availability"
      payload_available: "online"
      payload_not_available: "offline"
  
- platform: mqtt
  state_topic: 'formula1/evento/treino2'
  name: 'Treino 2'
  icon: mdi:calendar-clock
  availability:
    - topic: "formula1/evento/treino2/availability"
      payload_available: "online"
      payload_not_available: "offline"
  
- platform: mqtt
  state_topic: 'formula1/evento/treino3'
  name: 'Treino 3'
  icon: mdi:calendar-clock
  availability:
    - topic: "formula1/evento/treino3/availability"
      payload_available: "online"
      payload_not_available: "offline"
  
- platform: mqtt
  state_topic: 'formula1/evento/qualificacao'
  name: 'Qualificação'
  icon: mdi:calendar-clock
  availability:
    - topic: "formula1/evento/qualificacao/availability"
      payload_available: "online"
      payload_not_available: "offline"
  
- platform: mqtt
  state_topic: 'formula1/evento/corrida'
  name: 'Corrida'
  icon: mdi:calendar-clock
  availability:
    - topic: "formula1/evento/corrida/availability"
      payload_available: "online"
      payload_not_available: "offline"

- platform: mqtt
  state_topic: 'formula1/mundial_pilotos/1'
  name: 'Mundial Pilotos 1'
- platform: mqtt
  state_topic: 'formula1/mundial_pilotos/2'
  name: 'Mundial Pilotos 2'
- platform: mqtt
  state_topic: 'formula1/mundial_pilotos/3'
  name: 'Mundial Pilotos 3'

- platform: mqtt
  state_topic: 'formula1/mundial_construtores/1'
  name: 'Mundial Construtores 1'
- platform: mqtt
  state_topic: 'formula1/mundial_construtores/2'
  name: 'Mundial Construtores 2'

Para usar os avisos (telegram/Alexa) vamos precisar de um input_datetime:

  proximo_evento:
    name: Próximo Evento
    has_date: true
    has_time: true

As notificações você pode ajustar quando quer receber. Uma para cada treino, classificação e duas para a corrida. No nó Ajustar Alarme, só você alterar com quanto tempo antes quer receber cada notificação.

image

image

A função final vai entregar um payload com a mensagem formatada. Só você enviar para saída de seu interesse.

O card completo, incluindo o filtro para ir ocultando os eventos que já se passaram segue abaixo.

  - title: Formula 1
    path: formula-1
    icon: 'hass:flag-checkered'
    badges: []
    cards:
      - card:
          title: Próxima Corrida
          type: entities
          show_header_toggle: false
          show_toggle: false
          state_color: false
        entities:
          - entity: sensor.local
          - entity: sensor.treino_1
          - entity: sensor.treino_2
          - entity: sensor.treino_3
          - entity: sensor.qualificacao
          - entity: sensor.corrida
        filter:
          exclude:
            - state: unavailable
        show_empty: true
        type: 'custom:auto-entities'
      - type: markdown
        content: |-
          {{ states("sensor.mundial_pilotos_1") }}
          {{ states("sensor.mundial_pilotos_2") }}
          {{ states("sensor.mundial_pilotos_3") }}
        title: Mundial de Pilotos
      - type: markdown
        content: |-
          {{ states("sensor.mundial_construtores_1") }}
          {{ states("sensor.mundial_construtores_2") }}
        title: Mundial de Construtores

Espero que aproveitem.

Abraço

6 Likes

eu também não consegui

Fiz aqui e funcionou! Parabéns e obrigado!

2 Likes

Boa noite Leandro,

Estou usando a integração. Excelente!

Apenas como contribuição, se quiser atualizar o tópico, houve alteração nas configurações do yaml.

No caso do Flamengo:

########>>>>>>>>  SENSORES DE FUTEBOL - FLAMENGO (MULTISCRAPE)
multiscrape:
  - resource: https://www.flamengo.com.br/home
    scan_interval: 60
    sensor:
      - unique_id: flamengo_proximojogo_vs
        name: Próximo Jogo
        select: ".team-vs"
      - unique_id: flamengo_proximojogo_campeonato
        name: Campeonato
        select: ".championship"
      - unique_id: flamengo_proximojogo_datahora
        name: Data do Jogo
        select: ".event-time"
2 Likes

Atualizei lá! :grinning:

1 Like

Boa tarde Leandro, muito legal o componente MultiScrape!!

Para contribuir, adicionei o local do jogo.

Segue para caso alguém tenha interesse:

YAML:

  - unique_id: flamengo_proximojogo_local
    name: Local
    select: "p.text-uppercase:nth-child(4) > span:nth-child(1)"

Card:

  - entity: sensor.flamengo_proximojogo_local
    icon: mdi:stadium

Flamengo

2 Likes