Jogos de Futebol com transmissão televisiva v2

Boas malta, o anterior parser que tinha feito para obter os jogos de futebol com transmissão na TV deixou de funcionar e fiz outro apartir de outro site fonte.

Partilho o flow que fiz para obter um conjunto de dados como o card que fiz de forma a facilitar a leitura dos jogos entre os diferentes dias/competições.

O card tem vários buttons para selecionar o dia assim como o tipo de vista, para tal é necessário seguir as instruções na parte de configuração do card.

Devem instalar as seguintes bibliotecas no node red:
node-red-contrib-cron-plus
node-red-contrib-iconv

Flow a importar para o Node Red:
(devem configurar os nodes necessários de forma a executarem o flow e criar o sensor no Home Assistant)

[{"id":"30ee198fc9087217","type":"tab","label":"Futebol","disabled":false,"info":"","env":[]},{"id":"e01a42f439c105e8","type":"inject","z":"30ee198fc9087217","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":160,"wires":[["ac7e9165fbb8182e"]]},{"id":"ac7e9165fbb8182e","type":"http request","z":"30ee198fc9087217","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"https://www.zerozero.pt/zapping","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":290,"y":160,"wires":[["e9e914a8d34f99f7"]]},{"id":"e9e914a8d34f99f7","type":"converter","z":"30ee198fc9087217","name":"","from":"ISO-8859-1","x":460,"y":160,"wires":[["2162710daca98e23"]]},{"id":"bb49728e0dd5d749","type":"debug","z":"30ee198fc9087217","name":"debug 12","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1160,"y":160,"wires":[]},{"id":"2162710daca98e23","type":"html","z":"30ee198fc9087217","name":"tbody","property":"payload","outproperty":"payload","tag":"#page_main > div:nth-child(2) > table > tbody","ret":"html","as":"single","x":610,"y":160,"wires":[["01ecd02c959eaef3"]]},{"id":"01ecd02c959eaef3","type":"function","z":"30ee198fc9087217","name":"Get table rows","func":"let allRows = [];\nlet html = msg.payload[0];\nlet htmlToParse = html;\n\nwhile(htmlToParse.indexOf(\"<tr>\") > -1 && htmlToParse.indexOf(\"</tr>\") > -1) {\n    let currentRow = htmlToParse.substring(4, htmlToParse.indexOf(\"</tr>\"));\n\n   allRows.push( {\n       'row': currentRow,\n   });\n   htmlToParse = htmlToParse.substring(htmlToParse.indexOf(\"</tr>\")+5, htmlToParse.length);\n} \n\n\nreturn [{\n    'payload': \n    {\n        'allRows': allRows\n    }\n}, null];","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":160,"wires":[["a52ede8c0bbbfc24"]]},{"id":"a52ede8c0bbbfc24","type":"function","z":"30ee198fc9087217","name":"Get all games","func":"let allRows = msg.payload.allRows;\nlet games = [];\n\nlet remainingRow = null;\nlet urlBase = \"https://www.zerozero.pt\";\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\n\nfor (var i = 0; i < allRows.length; i++) {\n\n let row = allRows[i].row;\n    let game = {\n        'date'              : '',\n        'time'              : '',\n        'transmissionType'  : '',\n        'country'           : '',\n        'countryIcon'       : '',\n        'division'          : '',\n        'homeTeamName'      : '',\n        'homeTeamIcon'      : '',\n        'awayTeamName'      : '',\n        'awayTeamIcon'      : '',\n        'channel'           : '',\n        'channelIcon'       : '',\n        'channelLink'       : '',\n        'competitionName'   : ''\n    };\n\n    if (game.date == '') {\n        let gameTime = row.substring(row.indexOf(\";data=\") + 6, row.indexOf(\";data=\") + 25);\n        game.date =gameTime.split(\" \")[0];\n        game.time = gameTime.split(\" \")[1].substring(0, gameTime.split(\" \")[1].length - 3);\n        row = row.substring(row.indexOf(\";data=\") + 25, row.length);\n\n        remainingRow = row;\n    }\n    if (game.countryIcon == '') {\n        game.countryIcon = urlBase + row.substring(row.indexOf(\"img src=\") + 9, row.indexOf(\"alt=\") - 25);\n        game.country = row.substring(row.indexOf(\"alt=\") + 5, row.indexOf(\"title\") - 2);\n\n        row = row.substring(row.indexOf(\"title\"), row.length);\n        remainingRow = row;\n    }\n\n    if(game.division == '') {\n        game.division = row.substring(0, row.indexOf(\"</a>\")).substring(row.substring(0, row.indexOf(\"</a>\")).lastIndexOf(\">\")+1,row.substring(0, row.indexOf(\"</a>\")).length);\n      //  game.division = game.division.substring(0, game.division.lastIndexOf(\" \"));\n        row = row.substring(row.indexOf(\"</a>\"), row.length);\n        remainingRow = row;\n    }\n\n    if(game.homeTeamName == '')  {\n        game.homeTeamName = hexHtmlToString(row.substring(row.indexOf(\"alt=\") + 5, row.indexOf(\"title\") - 2));\n        game.homeTeamIcon = urlBase + row.substring(row.indexOf(\"src=\") + 5, row.indexOf(\"border=\") - 2);\n\n        row = row.substring(row.indexOf(\"border=\")+10, row.length);\n        remainingRow = row;\n\n        game.awayTeamIcon = urlBase+ row.substring(row.indexOf(\"src=\") + 5, row.indexOf(\"border=\") - 2);\n        row = row.substring(row.indexOf(\"vs\"), row.length);\n        remainingRow = row;\n    }\n\n     if(game.awayTeamName == '')  {\n         game.awayTeamName = hexHtmlToString(row.substring( row.substring(0, row.indexOf(\"<span\")).lastIndexOf(\">\")+1, row.indexOf(\"<span\")));\n \n        row = row.substring(row.indexOf('<span'), row.length);\n        remainingRow = row;\n    }\n\n    if(game.channel == '')  {\n        if(row.indexOf(\"href=\") < row.indexOf(\"<img\")) {\n            game.channelLink  =  row.substring(row.indexOf(\"href=\") + 6, row.indexOf(\"<img\") - 2);\n        }\n        \n          game.channelIcon = getChannelLink(game);\n          // urlBase+ row.substring(row.indexOf(\"src=\") + 5, row.indexOf(\"width=\")-2);\n            game.channel = row.substring(row.indexOf(\"alt=\") + 5, row.indexOf(\"title\") - 2);\n          game.competitionName = row.substring(row.substring(0, row.lastIndexOf(\"</a>\") - 2).lastIndexOf(\">\")+1, row.lastIndexOf(\"</a>\"));\n        game.competitionName = game.competitionName.substring(0, game.competitionName.lastIndexOf(\" \"));\n  \n        if( game.channel == \"Canal11\"){\n            game.channel = \"Canal 11\";\n            game.channelIcon = \"https://www.canal11.pt/images/customer/logo.png\";\n        } else if(game.channel.includes(\"11Sports\")) {\n            game.channel = game.channel.replace(\"11Sports\", \"Eleven Sports\");\n        \n        } else if (game.channel == \"SporTtv+\") {\n            game.channel = \"Sport TV +\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-mais-rebrand.svg\";\n        \n\n           } else if (game.channel == \"SportTv1\") {\n            game.channel = \"Sport TV1\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-1-rebrand.svg\";\n        \n\n           } else if (game.channel == \"SportTv2\") {\n            game.channel = \"Sport TV 2\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-2-rebrand.svg\";\n        \n\n           } else if (game.channel == \"SportTv3\") {\n            game.channel = \"Sport TV 3\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-3-rebrand.svg\";\n        \n\n           } else if (game.channel == \"SportTv4\") {\n            game.channel = \"Sport TV 4\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-4-rebrand.svg\";\n        \n\n        } else if (game.channel == \"SportTv5\") {\n            game.channel = \"Sport TV 5\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-5-rebrand.svg\";\n\n        } else if (game.channel == \"SportTv6\") {\n            game.channel = \"Sport TV 6\";\n            game.channelIcon = \"https://www.sporttv.pt/logos/sporttv-6-rebrand.svg\";\n        }  else if (game.channel == \"BTv\") {\n            game.channel = \"Benfica TV\";\n            game.channelIcon = \"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Logo_Benfica_TV.png/800px-Logo_Benfica_TV.png\";\n        }\n        \n        if (game.channel === \"Eleven Sports 1\") {\n            game.channelIcon = \"https://www.meo.pt/PublishingImages/canais/dazn-eleven-1-meo-logo.webp\";\n        }\n        if (game.channel === \"Eleven Sports 2\") {\n            game.channelIcon = \"https://www.meo.pt/PublishingImages/canais/dazn-eleven-2-meo-logo.webp\";\n        }\n        if (game.channel === \"Eleven Sports 3\") {\n            game.channelIcon = \"https://www.meo.pt/PublishingImages/canais/dazn-eleven-3-meo-logo.webp\";\n        }\n        if (game.channel == \"Eleven Sports 4\") {\n            game.channelIcon = \"https://www.meo.pt/PublishingImages/canais/dazn-eleven-4-meo-logo.webp\";\n        }\n        if (game.channel == \"Eleven Sports 5\") {\n            game.channelIcon = \"https://www.meo.pt/PublishingImages/canais/dazn-eleven-5-meo-logo.webp\";\n        }\n        if (game.channel == \"Eleven Sports 6\") {\n            game.channelIcon = \"https://www.meo.pt/PublishingImages/canais/dazn-eleven-6-meo-logo.webp\";\n        }\n\n        if (game.channel == \"RTP1\") {\n            game.channel = \"RTP 1\";\n            game.channelIcon = \"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/RTP1_-_Logo_2016.svg/2560px-RTP1_-_Logo_2016.svg.png\";\n        }\n\n        if (game.channel == \"RTP Play\") {\n            game.channelIcon = \"https://static.wikia.nocookie.net/logopedia/images/4/47/RTP_Play.svg/revision/latest?cb=20211109150238\";\n        }\n\n        if (game.channel == \"SIC\") {\n            game.channelIcon = \"https://upload.wikimedia.org/wikipedia/pt/5/51/Logo_SIC_2018.png\";\n        }\n\n         if (game.channel == \"TVI\") {\n            game.channelIcon = \"https://upload.wikimedia.org/wikipedia/en/6/63/TVI_logo_2017.png\";\n        }\n\n        if(game.channel == \"AFL TV\") {\n             game.channelIcon = \"https://afl.pt/wp-content/uploads/2023/10/AFL-TV__.png\";\n        }\n\n        \n\n       \n\n        \n    }\n    games.push(game);\n}\n\nfunction getChannelLink(game) {\n    let channelLink = \"\";\n\n    if(game.channel == \"Eleven Sports 4\") {\n        channelLink = \"canais/dazn-eleven-4-meo-logo.webp\";\n    }\n    \n    return channelLink;\n}\n\nfunction hexHtmlToString(str) {\n    if (str.includes(\"&#xE7;\")) {\n        str = str.replace(\"&#xE7;\", \"ç\");\n    }\n\n     if (str.includes(\"&#xE1;\")) {\n        str = str.replace(\"&#xE1;\", \"á\");\n    }\n\n    if (str.includes(\"&#xE3;\")) {\n        str = str.replace(\"&#xE3;\", \"ã\");\n    }\n\n    if (str.includes(\"&#xE9;\")) {\n        str = str.replace(\"&#xE9;\", \"é\");\n    }\n\n\n    if (str.includes(\"&#xF3;\")) {\n        str = str.replace(\"&#xF3;\", \"ó\");\n    }\n\n    \n    \n   return str;\n}\n\nfunction getTodayGames(gamesByDate) {\n    let today = new Date().toISOString().split('T')[0];\n    let todayGames = [];\n    for(var i=0; i<gamesByDate.length; i++) {\n        if(gamesByDate[i].date == today) {\n            todayGames = gamesByDate[i];\n            break;\n        }\n    }\n    return todayGames;\n}\n\nfunction getTomorrowGames(gamesByDate) {\n     var tomorrowDate = new Date();\n    tomorrowDate.setDate(tomorrowDate.getDate() + 1);\n    tomorrowDate = tomorrowDate.toISOString().split('T')[0];\n\n    let tomorrowGames = [];\n    for(var i=0; i<gamesByDate.length; i++) {\n        if(gamesByDate[i].date == tomorrowDate) {\n            tomorrowGames = gamesByDate[i];\n            break;\n        }\n    }\n    return tomorrowGames;\n}\n\nfunction getNextThreeDaysGames(gamesByDate) {\n    let nextThreeDays = [];\n    let today = new Date().toISOString().split('T')[0];\n    nextThreeDays.push(today);\n\n    var tomorrowDate = new Date();\n    tomorrowDate.setDate(tomorrowDate.getDate() + 1);\n    tomorrowDate = tomorrowDate.toISOString().split('T')[0];\n    nextThreeDays.push(tomorrowDate);\n\n     var nextTomorrowDate = new Date();\n    nextTomorrowDate.setDate(nextTomorrowDate.getDate() + 2);\n    nextTomorrowDate = nextTomorrowDate.toISOString().split('T')[0];\n    nextThreeDays.push(nextTomorrowDate);\n\n    let nextThreeDaysGames = [];\n    for(var i=0; i<gamesByDate.length; i++) {\n        if(nextThreeDays.includes(gamesByDate[i].date)) {\n           nextThreeDaysGames.push(gamesByDate[i]);\n        }\n    }\n    return nextThreeDaysGames;\n}\n\n\n\n\nfunction getGamesByDate(allGames) {\n    let gamesByDate = [];\n    for(var i=0; i<allGames.length; i++) {\n        if(gamesByDate.length ==0) {\n            gamesByDate.push({\n                'date' : allGames[i].date,\n                'games' : [allGames[i]],\n                'competitions' : [{\n                    'name' : allGames[i].competitionName,\n                    'icon': allGames[i].countryIcon,\n                    'games' :  [allGames[i]]\n                }\n                ]\n            });\n        } else {\n            let foundedDate = false;\n            let foundedCompetition = false;\n            for (var t = 0; t < gamesByDate.length; t++) {\n                if (gamesByDate[t].date ==  allGames[i].date) {\n                    foundedDate = true;\n                    gamesByDate[t].games.push(allGames[i]);\n                    for(var x=0; x<gamesByDate[t].competitions.length; x++) {\n                        if(gamesByDate[t].competitions[x].name ==allGames[i].competitionName ) {\n                            gamesByDate[t].competitions[x].games.push(allGames[i]);\n                            foundedCompetition = true;\n                        }\n                    }\n                    if(!foundedCompetition) {\n                        gamesByDate[t].competitions.push({\n                                'name' : allGames[i].competitionName,\n                                'icon' : allGames[i].countryIcon,\n                                'games' :  [allGames[i]]\n                            });\n                    }\n                }\n            }\n            if(!foundedDate) {\n                 gamesByDate.push({\n                'date' : allGames[i].date,\n                'games' : [allGames[i]],\n                'competitions' : [{\n                    'name' : allGames[i].competitionName,\n                    'icon' : allGames[i].countryIcon,\n                    'games' :  [allGames[i]]\n                }\n                ]\n            });\n            }\n        }\n    }\n    return gamesByDate;\n}\n\nlet gamesByDate = getGamesByDate(games);\nlet todayGames = getTodayGames(gamesByDate);\nlet tomorrowGames = getTomorrowGames(gamesByDate);\nlet nextThreeDaysGames = getNextThreeDaysGames(gamesByDate);\n\nreturn [{\n    'payload': {\n        'updatedOn' : dateStr,\n        'allGames': games,\n        'gamesByDate' : gamesByDate,\n        'todayGames' : todayGames,\n        'tomorrowGames' : tomorrowGames,\n        'nextThreeDaysGames' : nextThreeDaysGames\n    }\n}, null];","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":980,"y":160,"wires":[["bb49728e0dd5d749","956c0583d750ed41"]]},{"id":"956c0583d750ed41","type":"ha-sensor","z":"30ee198fc9087217","name":"TV FootbalGames","entityConfig":"c5c174550c444990","version":0,"state":"payload.updatedOn","stateType":"msg","attributes":[{"property":"todayGames","value":"payload.todayGames","valueType":"msg"},{"property":"tomorrowGames","value":"payload.tomorrowGames","valueType":"msg"},{"property":"updatedOn","value":"payload.updatedOn","valueType":"msg"},{"property":"nextThreeDaysGames","value":"payload.nextThreeDaysGames","valueType":"msg"},{"property":"gamesByDate","value":"payload.gamesByDate","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":1190,"y":260,"wires":[[]]},{"id":"70ab84cc965c1022","type":"cronplus","z":"30ee198fc9087217","name":"Every hour","outputField":"payload","timeZone":"","storeName":"","commandResponseMsgOutput":"output1","defaultLocation":"","defaultLocationType":"default","outputs":1,"options":[{"name":"schedule1","topic":"topic1","payloadType":"default","payload":"","expressionType":"cron","expression":"0 0 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":150,"y":60,"wires":[["ac7e9165fbb8182e"]]},{"id":"c5c174550c444990","type":"ha-entity-config","server":"b9ae8401.4dfb28","deviceConfig":"","name":"TV Footbal Games","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"TV Footbal Games"},{"property":"icon","value":"mdi:soccer"},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"b9ae8401.4dfb28","type":"server","name":"Home Assistant","addon":true}]

Devem testar a execução do flow para garantir que o sensor é criado com sucesso. O sensor criado tem o nome sensor.tv_footbal_games. Para validar, podem aceder ao Developer Tools e consultar o estado do sensor anterior para garantir que os dados foram carregados com sucesso.

Devem criar o seguinte template no ficheiro configuration.yaml

template:
  - sensor:
      - name: "Football TV Selected View Type"
        state: >
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            today_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'list') %}
            today_list
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            tomorrow_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'list') %}
            tomorrow_list
            {% endif %}
            {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            nextdays_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'list') %}
            nextdays_list  
          {% endif %}

A nivel do card, é necessário adicionarem os seguintes cards custom:

Para o card, criei 2 inputs helpers (aceder às Settings, integrations, helpers e criar com os nomes iguais) que permite guardar a selecção do dia e tipo de vista.
Criar dois helpers com os ids:

input_text.football_tv_view_type e input_text.football_tv_range_days

Código do Card:

type: custom:vertical-stack-in-card
cards:
  - type: custom:bootstrap-grid-card
    cards:
      - type: row
        cards:
          - type: custom:button-card
            class: col-xs-3 col-md-1 col-2 col-lg-1
            icon: mdi:calendar-today
            size: 60%
            styles:
              card:
                - height: 30px
            tap_action:
              action: call-service
              service: input_text.set_value
              target:
                entity_id: input_text.football_tv_range_days
              data:
                value: today
          - type: custom:button-card
            class: col-xs-3 col-md-2 col-2 col-lg-1
            icon: mdi:calendar-arrow-right
            size: 60%
            styles:
              card:
                - height: 30px
            tap_action:
              action: call-service
              service: input_text.set_value
              target:
                entity_id: input_text.football_tv_range_days
              data:
                value: tomorrow
          - type: custom:button-card
            class: col-xs-3 col-md-2 col-2 col-lg-1
            icon: mdi:calendar-end
            size: 60%
            styles:
              card:
                - height: 30px
            tap_action:
              action: call-service
              service: input_text.set_value
              target:
                entity_id: input_text.football_tv_range_days
              data:
                value: nextdays
          - type: custom:button-card
            color_type: blank-card
            class: col-xs-3 col-md-5 col-2 col-lg-7
          - type: custom:button-card
            class: col-xs-10 col-md-1 col-2 col-lg-1
            icon: mdi:format-list-bulleted
            size: 60%
            styles:
              card:
                - height: 30px
            tap_action:
              action: call-service
              service: input_text.set_value
              target:
                entity_id: input_text.football_tv_view_type
              data:
                value: list
          - type: custom:button-card
            class: col-xs-10 col-md-1 col-2 col-lg-1
            size: 60%
            styles:
              card:
                - height: 30px
            tap_action:
              action: call-service
              service: input_text.set_value
              target:
                entity_id: input_text.football_tv_view_type
              data:
                value: grid
            icon: mdi:order-bool-ascending
  - type: vertical-stack
    cards:
      - type: conditional
        conditions:
          - entity: sensor.football_tv_selected_view_type
            state: today_list
        card:
          type: custom:html-template-card
          ignore_line_breaks: true
          content: |
            <h2> Futebol na TV: <u>
              {{states.sensor.tv_footbal_games.attributes.todaygames.date }}</u>
              </h2>
               <table style="width: 100%; border: 1px solid; border-radius: 10px;">
              <tbody>
               {% for game in states.sensor.tv_footbal_games.attributes.todaygames.games -%}

               <tr>
               <td colspan="4" style=" text-align:center;"><img style=margin-top:10px;margin-right:10px;width:20px;border-radius:50px;height:20px" src={{game.countryIcon}} /><b>{{game.competitionName}} </b></td>

                <tr>
                
                  <td style="font-size:10px;text-align:center; width:10%">{{game.time}}</td>
                  <td> <div style="margin-left:10px;border-left:1px solid;height:30px"></div></td>
                  <td >
                      <div style="margin-top:10px; margin-bottom:5px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.homeTeamIcon}} /><label style="margin-left:10px">{{game.homeTeamName}} </label> </div>
                      <div style="margin-bottom:10px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.awayTeamIcon}} /><label style="margin-left:10px">{{game.awayTeamName}} </label> </div>
                  
                  </td>
                  <td style="width:20%; text-align:center">
                    <div ><img
              style="max-height:30px; max-width:70px; height:auto;width:auto;"
              src={{game.channelIcon}} /><div>
                    <div style="font-size:8px;">{{game.channel}}</div>
                  </td>
              </tr>

              {%- endfor %}  <tbody> </table>
      - type: conditional
        conditions:
          - entity: sensor.football_tv_selected_view_type
            state: today_grid
        card:
          type: custom:html-template-card
          title: null
          ignore_line_breaks: true
          content: >
            <h2> Futebol na TV: <u>
            {{states.sensor.tv_footbal_games.attributes.todaygames.date }}</u>
            </h2>

             {% for competition in states.sensor.tv_footbal_games.attributes.todaygames.competitions -%}
               <table style="margin-top:15px; width: 100%; border: 1px solid; border-radius: 10px;">
            <tbody>
             <tr>
             <td colspan="4" style=" text-align:center;"><img style=margin-top:10px;margin-right:10px;width:20px;border-radius:50px;height:20px" src={{competition.icon}} /><b>{{competition.name}} </b></td>
                
              </tr>
              {% for game in competition.games -%}
              <tr>
                
                  <td style="font-size:10px;text-align:center; width:10%">{{game.time}}</td>
                  <td> <div style="margin-left:10px;border-left:1px solid;height:30px"></div></td>
                  <td >
                      <div style="margin-top:10px; margin-bottom:5px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.homeTeamIcon}} /><label style="margin-left:10px">{{game.homeTeamName}} </label> </div>
                      <div style="margin-bottom:10px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.awayTeamIcon}} /><label style="margin-left:10px">{{game.awayTeamName}} </label> </div>
                  
                  </td>
                  <td style="width:20%;text-align:center">
                    <div ><img
              style="max-height:30px; max-width:70px; height:auto;width:auto;"
              src={{game.channelIcon}} /><div>
                    <div style="font-size:8px;">{{game.channel}}</div>
                  </td>
              </tr>
              {%- endfor %}  
            <tbody> </table> {%- endfor %}  
      - type: conditional
        conditions:
          - entity: sensor.football_tv_selected_view_type
            state: tomorrow_list
        card:
          type: custom:html-template-card
          ignore_line_breaks: true
          content: |
            <h2> Futebol na TV: <u>
              {{states.sensor.tv_footbal_games.attributes.tomorrowgames.date }}</u>
              </h2>
               <table style="width: 100%; border: 1px solid; border-radius: 10px;">
              <tbody>
               {% for game in states.sensor.tv_footbal_games.attributes.tomorrowgames.games -%}

               <tr>
               <td colspan="4" style=" text-align:center;"><img style=margin-top:10px;margin-right:10px;width:20px;border-radius:50px;height:20px" src={{game.countryIcon}} /><b>{{game.competitionName}} </b></td>

                <tr>
                
                  <td style="font-size:10px;text-align:center; width:10%">{{game.time}}</td>
                  <td> <div style="margin-left:10px;border-left:1px solid;height:30px"></div></td>
                  <td >
                      <div style="margin-top:10px; margin-bottom:5px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.homeTeamIcon}} /><label style="margin-left:10px">{{game.homeTeamName}} </label> </div>
                      <div style="margin-bottom:10px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.awayTeamIcon}} /><label style="margin-left:10px">{{game.awayTeamName}} </label> </div>
                  
                  </td>
                  <td style="width:20%;text-align:center">
                    <div ><img
              style="max-height:30px; max-width:70px; height:auto;width:auto;"
              src={{game.channelIcon}} /><div>
                    <div style="font-size:8px;">{{game.channel}}</div>
                  </td>
              </tr>

              {%- endfor %}  <tbody> </table>
      - type: conditional
        conditions:
          - entity: sensor.football_tv_selected_view_type
            state: tomorrow_grid
        card:
          type: custom:html-template-card
          ignore_line_breaks: true
          content: >
            <h2> Futebol na TV: <u>
            {{states.sensor.tv_footbal_games.attributes.tomorrowgames.date
            }}</u> </h2>

             {% for competition in states.sensor.tv_footbal_games.attributes.tomorrowgames.competitions -%}
               <table style="margin-top:15px; width: 100%; border: 1px solid; border-radius: 10px;">
            <tbody>
             <tr>
             <td colspan="4" style=" text-align:center;"><b>{{competition.name}} </b></td>
                
              </tr>
              {% for game in competition.games -%}
              <tr>
                
                  <td style="font-size:10px;text-align:center; width:10%">{{game.time}}</td>
                  <td> <div style="margin-left:10px;border-left:1px solid;height:30px"></div></td>
                  <td >
                      <div style="margin-top:10px; margin-bottom:5px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.homeTeamIcon}} /><label style="margin-left:10px">{{game.homeTeamName}} </label> </div>
                      <div style="margin-bottom:10px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.awayTeamIcon}} /><label style="margin-left:10px">{{game.awayTeamName}} </label> </div>
                  
                  </td>
                  <td style="width:20%;text-align:center">
                    <div ><img
              style="max-height:30px; max-width:70px; height:auto;width:auto;"
              src={{game.channelIcon}} /><div>
                    <div style="font-size:8px;">{{game.channel}}</div>
                  </td>
              </tr>
              {%- endfor %}  
            <tbody> </table> {%- endfor %}  
      - type: conditional
        conditions:
          - entity: sensor.football_tv_selected_view_type
            state: nextdays_list
        card:
          type: custom:html-template-card
          ignore_line_breaks: true
          content: >
            {% for gameDate in
            states.sensor.tv_footbal_games.attributes.gamesbydate -%}
                <h2>{{gameDate.date}}</h2>
                   <table style="width: 100%; border: 1px solid; border-radius: 10px;">
             <tbody>
                  {% for game in gameDate.games -%}
                  

              <tr>
              <td colspan="4" style=" text-align:center;"><img style=margin-top:10px;margin-right:10px;width:20px;border-radius:50px;height:20px" src={{game.countryIcon}} /><b>{{game.competitionName}} </b></td>

               <tr>
               
                 <td style="font-size:10px;text-align:center; width:10%">{{game.time}}</td>
                 <td> <div style="margin-left:10px;border-left:1px solid;height:30px"></div></td>
                 <td >
                     <div style="margin-top:10px; margin-bottom:5px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.homeTeamIcon}} /><label style="margin-left:10px">{{game.homeTeamName}} </label> </div>
                     <div style="margin-bottom:10px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.awayTeamIcon}} /><label style="margin-left:10px">{{game.awayTeamName}} </label> </div>
                 
                 </td>
                 <td style="width:20%;text-align:center">
                   <div ><img
             style="max-height:30px; max-width:70px; height:auto;width:auto;"
             src={{game.channelIcon}} /><div>
                   <div style="font-size:8px;">{{game.channel}}</div>
                 </td>
             </tr>
                   {%- endfor %} 
                    <tbody> </table>
              {%- endfor %} 
      - type: conditional
        conditions:
          - entity: sensor.football_tv_selected_view_type
            state: nextdays_grid
        card:
          type: custom:html-template-card
          ignore_line_breaks: true
          content: |
            {% for gameDate in
             states.sensor.tv_footbal_games.attributes.gamesbydate -%}
                 <h2>{{gameDate.date}}</h2>
                   
                   {% for competition in gameDate.competitions -%}
                    <table style="margin-top:15px; width: 100%; border: 1px solid; border-radius: 10px;">
                    <tbody>
              <tr>
              <td colspan="4" style=" text-align:center;"><b>{{competition.name}} </b></td>
                 
               </tr>
                      {% for game in competition.games -%}
               <tr>
                 
                   <td style="font-size:10px;text-align:center; width:10%">{{game.time}}</td>
                   <td> <div style="margin-left:10px;border-left:1px solid;height:30px"></div></td>
                   <td >
                       <div style="margin-top:10px; margin-bottom:5px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.homeTeamIcon}} /><label style="margin-left:10px">{{game.homeTeamName}} </label> </div>
                       <div style="margin-bottom:10px"><img style="vertical-align:middle;width:20px;height:20px" src={{game.awayTeamIcon}} /><label style="margin-left:10px">{{game.awayTeamName}} </label> </div>
                   
                   </td>
                   <td style="width:20%;text-align:center">
                     <div ><img
               style="max-height:30px; max-width:70px; height:auto;width:auto;"
               src={{game.channelIcon}} /><div>
                     <div style="font-size:8px;">{{game.channel}}</div>
                   </td>
               </tr>
               {%- endfor %}
                    </table>
                     {%- endfor %} 
               {%- endfor %} 

Além disto é preciso criar o seguinte sensor no ficheiro configuration.yaml para guardar a seleção referente ao tipo de vista/dia.

template:
  - sensor:
      - name: "Football TV Selected View Type"
        state: >
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            today_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'list') %}
            today_list
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            tomorrow_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'list') %}
            tomorrow_list
            {% endif %}
            {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            nextdays_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'list') %}
            nextdays_list  
          {% endif %}


5 Curtiram

Logo já experimento mas parece brutal. Obrigado pela partilha :muscle:

1 Curtiu

Boas.

Já tentei instalar mas não aparece nada.
Apesar de faltar de mencionar um addon do HACS (bootstrap-grid-card), não aparece nada.
Instalei as bibliotecas do NR e não me cria nenhum sensor.football_tv_selected_view_type.

Fico assim.
image

Pode dar-me uma ajuda por favor?
Obrigado desde já

Boas.

Antes de mais obrigado pelo feedback.
Deves validar se o flow do Node Red foi executado e se no último node mostra alguma data.

Atualizei o post inicial com a informação complementar para validar se o sensor ficou bem criado.

Sim o flow foi executado conforme mostra a imagem.

Mas o sensor não é criado…

Edita o ultimo node para ver os detalhes.

Tenho assim:

O nome do sensor deverá ser o sensor que ele cria no flow.

Refere-se a isto?

sim mas está ok.

Faz assim

  1. Aceder às Settings
  2. Devices & services
  3. Selecionar a integração Node Red Companion
  4. Aceder às entidades
  5. Consultar o sensor criado (sensor.tv_footbal_games) senão tiver este nome, consultar os outros sensores para ver se o Node Red aplicou outro nome.

Afinal os macacos estão aqui com outro nome…

porque andava à procura deste nome… não sei se isso faz diferença!!!

image

Txiii esqueci-me de outro sensor que é criado no configuration.yaml

Adiciona isto, dá restart e testa.

template:
  - sensor:
      - name: "Football TV Selected View Type"
        state: >
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            today_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'list') %}
            today_list
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            tomorrow_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'list') %}
            tomorrow_list
            {% endif %}
            {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            nextdays_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'list') %}
            nextdays_list  
          {% endif %}

Já está a dar.
Muito obrigado

1 Curtiu

Obrigado pelo feedback. Desta forma partilhei os dados no post inicial para que outros users consigam ler e colocar isto a bombar

Boas.

Tentei instalar também mas a mim os unicos sensores que tenho são estes :

No NodeRed não tenho erro nenhum, e no card quando carrego num dos botões não faz nada:
image

Sou eu que tenho de criar estas entidades?

@karma falta esta parte no configuration. Tenho de colocar no post inicial

Adiciona isto, dá restart e testa.

template:
  - sensor:
      - name: "Football TV Selected View Type"
        state: >
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            today_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'today') and
          is_state('input_text.football_tv_view_type', 'list') %}
            today_list
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            tomorrow_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'tomorrow') and
          is_state('input_text.football_tv_view_type', 'list') %}
            tomorrow_list
            {% endif %}
            {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'grid') %}
            nextdays_grid
            {% endif %}
          {% if is_state('input_text.football_tv_range_days', 'nextdays') and
          is_state('input_text.football_tv_view_type', 'list') %}
            nextdays_list  
          {% endif %}

Sim, esse eu já criei (como mostro na foto em cima), já me aparece esse sensor. Ele não está propriamente no configuration, mas está dentro dos pakages que é a mesma coisa. Eu já o tenho criado, mas nao apresenta valor nenhum, é mesmo assim?

Os “input_text.***”, temos que os criar?

Sim os input texto é preciso criar. Sempre para guardar o tipo de vista.

Partilha essa configuração sff. E adiciona em cima na partilha de projetos. Como é informação que falta para conseguirmos ter o card a funcionar :+1:

Já atualizei com os passos que deves seguir para ir á parte dos helpers e criar dois com os nomes indicados.

Amigo, não adicionas te essa configuração.

Para o card, criei 2 inputs helpers (aceder às Settings, integrations, helpers e criar com os nomes iguais) que permite guardar a selecção do dia e tipo de vista.
Criar dois helpers com os ids:

input_text.football_tv_view_type e input_text.football_tv_range_days

Está nesta parte. Tem de ser criado manualmente.

1 Curtiu

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


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