Como adepto de futebol, procurei uma maneira simples de saber quais os jogos com transmissão para o dia atual e o dia seguinte.
Através do Node Red e utilizando um scrape com base no site https://www.futebol365.pt/jogos-na-tv/, saquei a informação dos jogos, criei 2 entidades no HA com a info anterior e através de um template mostro a hora, jogo e canal.
Vamos por partes:
- Importar o seguinte flow para o Node Red e instalar a pallete node-red-contrib-cron-plus
Este flow está configurado para ser executado de hora em hora de forma a atualizar os dados dos seguintes sensores:
sensor:sensor.today_footbal_games
sensor.tomorrow_footbal_games
Através do node Inject, podem executar o flow e os sensores já ficam com dados.
[{"id":"b2b3b890.cbc99","type":"tab","label":"Futebol TV Sensores","disabled":false,"info":""},{"id":"ef5efef0.d8e0a8","type":"ha-entity","z":"b2b3b890.cbc99","name":"","server":"97033d39.2965b","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Today Footbal Games"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload[0].date","stateType":"msg","attributes":[{"property":"date","value":"payload[0].date","valueType":"msg"},{"property":"games","value":"payload[0].games","valueType":"msg"},{"property":"updatedOn","value":"payload[0].updatedOn","valueType":"msg"}],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":850,"y":520,"wires":[[]]},{"id":"f6800d57.400f48","type":"ha-entity","z":"b2b3b890.cbc99","name":"","server":"97033d39.2965b","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Tomorrow Footbal Games"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload[1].date","stateType":"msg","attributes":[{"property":"date","value":"payload[1].date","valueType":"msg"},{"property":"games","value":"payload[1].games","valueType":"msg"},{"property":"updatedOn","value":"payload[1].updatedOn","valueType":"msg"}],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":1030,"y":360,"wires":[[]]},{"id":"a1263a91.9e7468","type":"inject","z":"b2b3b890.cbc99","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":420,"wires":[["3f072ab4.ba1496"]]},{"id":"3f072ab4.ba1496","type":"http request","z":"b2b3b890.cbc99","name":"Obter Jogos na TV","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://www.futebol365.pt/jogos-na-tv/","tls":"","persist":false,"proxy":"","authType":"","x":350,"y":260,"wires":[["d2136d3d.26309","d4859fee.65b8e8"]]},{"id":"d4859fee.65b8e8","type":"html","z":"b2b3b890.cbc99","name":"","property":"payload","outproperty":"payload","tag":".condensed-400.headerTitle.orange","ret":"html","as":"single","x":710,"y":180,"wires":[["78037343.d22454"]]},{"id":"d2136d3d.26309","type":"html","z":"b2b3b890.cbc99","name":"","property":"payload","outproperty":"payload","tag":".ink-table.alternating.ink-table-f365.double-bottom-space tbody","ret":"text","as":"single","x":750,"y":220,"wires":[["4bbff48a.d32534"]]},{"id":"e56cba68.7d367","type":"cronplus","z":"b2b3b890.cbc99","name":"","outputField":"payload","timeZone":"","persistDynamic":false,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"schedule1","topic":"schedule1","payloadType":"default","payload":"","expressionType":"cron","expression":"0 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":120,"y":280,"wires":[["3f072ab4.ba1496"]]},{"id":"b4f32ab2.973cc8","type":"join","z":"b2b3b890.cbc99","name":"Dates With Games","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":480,"y":360,"wires":[["831be36f.94dad8"]]},{"id":"831be36f.94dad8","type":"function","z":"b2b3b890.cbc99","name":"Merge Dates/Games","func":"var allGamesArr = msg.payload[0];\nvar allDatesArr = msg.payload[1].dates;\n\nvar allGamesByDate = [];\n\nvar date = new Date();\nvar dateStr =\n date.getFullYear()+ \"-\" +\n (\"00\" + (date.getMonth() + 1)).slice(-2) + \"-\" +\n (\"00\" + date.getDate()).slice(-2) + \" \" +\n (\"00\" + date.getHours()).slice(-2) + \":\" +\n (\"00\" + date.getMinutes()).slice(-2) + \":\" +\n (\"00\" + date.getSeconds()).slice(-2);\n\n\n\nfor(var i=0; i<allDatesArr.length; i++) {\n \n var dateInfo = {\n 'date' : allDatesArr[i].gamesDate,\n 'games' : [],\n 'updatedOn' :dateStr\n };\n \n var dayGames = allGamesArr[i].games;\n for(var t=0; t<dayGames.length; t++) {\n dateInfo.games.push(dayGames[t]);\n }\n allGamesByDate.push(dateInfo);\n \n \n}\n\nreturn [{'payload': allGamesByDate}, null];","outputs":1,"noerr":0,"initialize":"","finalize":"","x":720,"y":360,"wires":[["f6800d57.400f48","ef5efef0.d8e0a8"]]},{"id":"78037343.d22454","type":"function","z":"b2b3b890.cbc99","name":"Get Dates","func":"var currentDate = msg.payload[0];\n\nvar dates = [];\n\nfor(var i=0; i<msg.payload.length; i++) {\n \n \n var dayNumber = msg.payload[i].substring(\n msg.payload[i].indexOf(\",\")+2, msg.payload[i].indexOf(\" de\"));\n \n \n var monthName = msg.payload[i].substring(\n msg.payload[i].indexOf(\" de\")+4,\n msg.payload[i].length\n );\n \n var gamesDate = \"\";\n \n var monthNumber = -1;\n if(monthName == \"Janeiro\") {\n monthNumber = \"01\";\n } else if(monthName == \"Fevereiro\") {\n monthNumber = \"02\";\n } else if(monthName.indexOf(\"Mar\")) {\n monthNumber = \"03\";\n } else if(monthName == \"Abril\") {\n monthNumber = \"04\";\n } else if(monthName == \"Maio\") {\n monthNumber = \"05\";\n } else if(monthName == \"Junho\") {\n monthNumber = \"06\";\n } else if(monthName == \"Julho\") {\n monthNumber = \"07\";\n } else if(monthName == \"Agosto\") {\n monthNumber = \"08\";\n } else if(monthName == \"Setembro\") {\n monthNumber = \"09\";\n } else if(monthName == \"Outubro\") {\n monthNumber = \"10\";\n } else if(monthName == \"Novembro\") {\n monthNumber = \"11\";\n } else if(monthName == \"Dezemrbo\") {\n monthNumber = \"12\";\n } \n gamesDate = new Date().getFullYear()+\"-\"+monthNumber+\"-\"+dayNumber;\n \n dates.push({\n 'date' : msg.payload[i],\n 'dayNumber' : dayNumber,\n 'monthName' : monthName,\n 'year' : new Date().getFullYear(),\n 'index': i,\n 'gamesDate' : gamesDate\n })\n}\nreturn [{'payload': {'dates': dates}}, null];","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1130,"y":180,"wires":[["b4f32ab2.973cc8"]]},{"id":"4bbff48a.d32534","type":"function","z":"b2b3b890.cbc99","name":"Get Games","func":"var allGames = [];\n\n\nfor(t=0; t<msg.payload.length; t++) {\n var dayGamesArr = msg.payload[t].split(/\\r\\n|\\r|\\n/);\n \n var dayObj = {\n 'gameIndex': t,\n 'games' : []\n };\n \n for(x=0; x<dayGamesArr.length; x++) {\n \n if(dayGamesArr[x] != null && dayGamesArr[x].length == 5 && dayGamesArr[x].indexOf(\":\") == 2 ) {\n dayObj.games.push({\n 'time' : dayGamesArr[x],\n 'homeTeam' : dayGamesArr[x+1],\n 'awayTeam' : dayGamesArr[x+3],\n 'channel' : dayGamesArr[x+7]\n });\n }\n }\n allGames.push(dayObj);\n\n}\n\n\nreturn [{'payload': allGames}, null];","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1110,"y":240,"wires":[["b4f32ab2.973cc8"]]},{"id":"97033d39.2965b","type":"server","name":"HomeAssistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]
Executem o flow e validem se no Developer Tools os dados estão como na seguinte imagem:
- Criar um Markdown para mostrar os jogos do dia atual com a seguinte informação:
> type: markdown
content: |
Futebol na TV: {{state_attr('sensor.today_footbal_games','date') }}
<font size="1">Atualizado a: {{state_attr('sensor.today_footbal_games','updatedon') }} </font>
<hr>
{% for game in state_attr('sensor.today_footbal_games','games') %}
[{{game.time}}] {{game.homeTeam}} vs {{game.awayTeam}} [{{game.channel}}]
{% endfor %}
Resultado final do card:
Podem replicar o card para mostrar os jogos do dia seguinte, alterando apenas o nome do sensor.