Flex-table-card

Thanks for the answer.
The first column gives a better result, but is still wrong:

type: custom:flex-table-card
sort_by: entries
entities:
  include: sensor.rss_feed*
columns:
  - name: ''
    data: entries
    modify: x.published
  - name: Video
    data: entries
    modify: >-
      '<a href="' + x.link +'" target="_blank"><img src="' +
      x.media_thumbnail[0].url + '"style="width: 10%"></a>'

flex-sort

First or second column - does not matter. Sorting is same. See a possible solution above.

I’m sorry I don’t see what’s wrong? That looks sorted to me by date correctly in the first column unless you want newest first then change your sort - https://github.com/custom-cards/flex-table-card/blob/0f0260f170660309906358254b8bb1ed7c301f63/docs/example-cfg-sorting-strict.md.

Or are you saying that the date is getting pulled incorrectly? If so that is not a flex-table problem.

1 post

the attribute is “published: 08.10.24 16:00”

Can you please describe it better?
I don’t quite understand what needs to be done.
Thank you very much!

You need to parse a value “DD.MM.YY hh:mm” and convert to a timestamp. Have no idea how it can be done in JS.

I am having an issue that I cannot figure out dealing with the Sports Standings that makes heavy use of flex table.

One field breaks the page with the following error:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'displayValue')
    at eval (eval at <anonymous> (flex-table-card.js?hacstag=156292058077:413:42), <anonymous>:1:44)
    at flex-table-card.js?hacstag=156292058077:413:42
    at Array.map (<anonymous>)
    at DataRow.render_data (flex-table-card.js?hacstag=156292058077:402:35)
    at flex-table-card.js?hacstag=156292058077:122:47
    at Array.map (<anonymous>)
    at DataTable.add (flex-table-card.js?hacstag=156292058077:122:32)
    at flex-table-card.js?hacstag=156292058077:708:26
    at Array.forEach (<anonymous>)
    at FlexTableCard._fill_card (flex-table-card.js?hacstag=156292058077:704:18)
eval @ VM39381:1
(anonymous) @ flex-table-card.js?hacstag=156292058077:413
render_data @ flex-table-card.js?hacstag=156292058077:402
(anonymous) @ flex-table-card.js?hacstag=156292058077:122
add @ flex-table-card.js?hacstag=156292058077:122
(anonymous) @ flex-table-card.js?hacstag=156292058077:708
_fill_card @ flex-table-card.js?hacstag=156292058077:704
set hass @ flex-table-card.js?hacstag=156292058077:691
_createCard @ decluttering-card.js?hacstag=188686483100:192
Promise.then
set hass @ decluttering-card.js?hacstag=188686483100:186
value @ hui-card.ts:164
performUpdate @ reactive-element.ts:1329
scheduleUpdate @ reactive-element.ts:1261
_$Ej @ reactive-element.ts:1233Understand this error

This field is within a larger set of things, only showing a snippet here:

        - name: <div>HOME</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'HOME').displayValue
        - name: <div>AWAY</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'AWAY').displayValue
        - name: <div>GF</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'GF').value
        - name: <div>GA</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'GA').value
        - name: <div>DIFF</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'DIFF').value
        - name: <div>Last</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'L10').displayValue
        - name: <div>STRK</div>
          data: '[[attribute]]'
          modify: x.stats.find(y=>y["abbreviation"] == 'STRK').value

Focusing on the data, again a snippet, it looks like this:

    {
        "id": "2",
        "name": "Home",
        "abbreviation": "HOME",
        "displayName": "Home",
        "shortDisplayName": "HOME",
        "description": "Home Record",
        "type": "home",
        "summary": "1-0-0",
        "displayValue": "1-0-0"
      },
      {
        "id": "3",
        "name": "Road",
        "abbreviation": "AWAY",
        "displayName": "Away",
        "shortDisplayName": "AWAY",
        "description": "Away Record",
        "type": "road",
        "summary": "0-0-0",
        "displayValue": "0-0-0"
      },
      {
        "id": "5",
        "name": "Last Ten Games",
        "abbreviation": "L10",
        "displayName": "Last Ten Games",
        "shortDisplayName": "L10",
        "description": "Record last 10 games",
        "type": "lasttengames",
        "summary": "1-0-0",
        "displayValue": "1-0-0, 0 PTS"
      },

As you can see from the above … the fields HOME, AWAY and L10 are all nearly the same in construction, at the same level. If I include L10 in the decluttering template, it errors as above and nothing is displayed. If I remove just L10, everyything works (obviously without the L10 field).

I have tried instead to use the “type” thinking that “L10” was bad, but it also does not work. I tried changing the name from “L10” but that does not work. THere is just something in that “L10” that is breaking flex-table with that error.

I can provide any other diagnostics I can, but I have run out of ideas.

The error itself does not mention “L10”. It mentions “displayName”, you have 3 items with this attribute.
Excude other 2 values from the card, leave only L10 - what will you see?

Yes, it is the L10 field and underneath the “displayValue” is the attribute.
Hang on for more, I think I know the issue.

Typical ESPN, it appears the “L10” field is missing on teams that have yet to play a game which should end in a few days as the season just started (or I have other JS for that to catch a bad field that I can wrap it).

And HOME, AWAY fields that are identically formatted and in the data returned from ESPN but exist at season start. Just L10 (which are the last 10 games) does not appear until they have played a game.

You are happy man )))
So, to solve the issue you need to check a presence of this entry.

Yep.

        - name: <div>L10</div>
          data: '[[attribute]]'
          modify: >-
            if(typeof x.stats.find(y=>y.abbreviation == 'L10') !== 'undefined'
            ){x.stats.find(y=>y["abbreviation"] == 'L10').summary}else{'-'}

Works a charm.

I would note that long ago I posted an enhancement request that I would have used. Using a plugin I would actually apply a function to all the fields and pass in field name and the attribute I wanted. I tested and it works great but it gets wiped after every update so I stopped using it.

2 Likes

I made use of flex table card to keep track of my many labels that are applied to entities, devices, automations and more.

1 Like

I have a table with some customized row colors. Is there a way to choose a different color when the user is in dark mode?

So, for example I have this:

css:
  tbody tr:nth-child(-n+8): "background-color: #d4edda !important"

It is possible by card-mod - you will have to use card-mod for THIS particular style instead of a native “css” option.
Described here: main card-mod thread - 1st post - link at the bottom - others - how to change styles in light and dark modes

Is there a known issue with the sorting of monetary values or am I misusing it?

If I leave the card alone (and let sort_by: state+ do its thing), It works. But if I click on the headers, I don’t get the right sorting:

The “Distance” column sorts properly. The last one (which displays the age of the information) doesn’t (even if I remove the prefix and show only numbers:

image

The sorting by price column doesn’t work either if I remove sort_by: state+

type: custom:flex-table-card
sort_by: state+
clickable: true
max_rows: 5
entities:
  include:
    - sensor.station_*SP98
columns:
  - data: entity_picture
    align: center
    name: ""
    modify: "'<img src=\"' + x + '\"style=\"height: 35px\">'"
  - data: fuel_type
    align: center
    name: ""
    icon: mdi:gas-station
  - data: address,city
    multi_delimiter: ", "
    align: left
    name: ""
  - icon: mdi:currency-eur
    data: state
    name: ""
    align: center
  - data: distance
    modify: x + " km"
    name: Distance
  - icon: mdi:calendar-clock
    name: ""
    data: days_since_last_update
    align: center
    prefix: J+
layout_options:
  grid_columns: full

I’d like column 5 to be column 4 multiplied by 43 (the price of a liter multiplied by the capacity = the price of a full tank).

It works if I don’t use auto_format :

Not with it true:

Just in case, I tried :

I’m sure some of you will tell me it works exactly as expected but I can assure you, as a user (and not a Javascript developer or a computer wizard of some sort), it’s extremely disconcerting.

So I imagine I should turn auto_format off and then apply a modify: to each column to replace the . with a , and add the unit?

Area and device are supported in the latest commit to the master branch. A new release is not yet available, but you can safely grab from master. The docs have been updated.

1 Like

This option has a wrong indentation, it must belong to “flex-table-card”.

This variable cannot be used here since “flex-table-card” does not contain an “entity” option.
Do not copy/paste a code from other cards blindly…

1 Like

Styling dependently on an entity value (and other entities if needed):

It is possible to compose a cell’s content dependently on an entity’s state/attribute (example):

  1. We can compose a cell belonging to a state - and dependently on this state.
  2. We can compose a cell belonging to an attribute - and dependently on this attribute (some more possibilities are available if an attribute is a list or a dictionary, but let’s not consider this for simplicity).

“Compose” means “add a ha-icon element”, “add a span element”, “set css property”.

Here is a way to use card-mod dependently on a particular row’s entity state/attributes:
Assume there is a simple table:
image

type: custom:auto-entities
filter:
  include:
    - entity_id: sensor.ac66u_snmp_*_temp
card:
  type: custom:flex-table-card
  columns:
    - name: ""
      data: icon
    - name: name
      data: friendly_name
    - name: value
      data: state

We need to style smth dependently on an entity’s state/attributes - like an icon’s & value’s color dependently on a value.
If a placement of entities is fixed - i.e. if “sensor.xxx” is always in the 1st row, “sensor.yyy” is always in the 2nd row etc - it is possible to use fixed CSS selectors like:

        tbody tr td:nth-child(1) ha-icon {
          {% if states('sensor.xxx')|float(default=0)  > ... -%}
          --icon-primary-color: red;
          {%- else -%}
          --icon-primary-color: green;
          {%- endif %}
        }

But a placement may be random, may change after sorting etc.
Luckily every row has an id containing an entity_id - and this helps to do this:

  card_mod:
    style: |
      {% for ROW_INDEX in range(config.entities|count) -%}
        {%- set ENTITY = config.entities[ROW_INDEX].entity -%}
        {%- set ID = 'entity_row_' + ENTITY -%}
        {%- if states(ENTITY)|float(default=0) > 55 -%}
          {%- set COLOR = 'red' -%}
        {%- else -%}
          {%- set COLOR = 'green' -%}
        {%- endif %}
        tbody tr[id^='{{ID}}'] td:nth-child(1) ha-icon {
          --icon-primary-color: {{COLOR}};
        }
        tbody tr[id^='{{ID}}'] td:nth-child(3) {
          color: {{COLOR}};
        }
      {% endfor %}

which causes this styling independently on a current sorting:
image

In this particular example a styling depends on THIS entity’s state/attributes - but ofc ANY other entity may be involved ))).

1 Like

First time on the forum so hope you’ll forgive any newbie errors. Also my coding knowledge is limited.

I have been aiming to introduce some network information within HA. I tried the very good home assistant network scanner but this relies on information supplied by the router. I am using Sky and the router they provided allows for very limited configuration. So the output from Network Scanner is based on poor info in this case. The router and mesh are on my list for replacing with a ‘proper’ router/mesh system but not sure when it will rise up my priority list…

Network scanner has an example using flex-table-card and that lead me to this post and the info here and elsewhere in the community got me to the point where I have a working system based around Fing and their API. I have the premium version which gives you everything they have but the API does come with the basic free version although I think this will only update manually and a max of 3 times a day - so maybe of limited use.

So this is my experience of how to do this - which I am sure could be streamlined/simplified or approached in other ways. I couldn’t find any examples for Fing so this may give some people a starting point.

Obviously you need to install flex-table-card to use and enable the Fing API from within Fing.

There are three elements:

A sensor to retrieve the data from the API

sensor:  
- platform: rest
  name: fing_api_data
  resource: http://192.168.1.xxx:49090/1/devices?auth=authorisationkeyfromapp  # Replace with actual API endpoint
  json_attributes:
    - devices  
  value_template: "OK"  

A template sensor to transform the JSON into something flex understands:

template:
  - sensor:
      - name: "Fing Devices Table"
        state: "Device Data"
        attributes:
          devices: >
            {% set devices = state_attr('sensor.fing_api_data', 'devices') %}
            {% if devices %}
              {{ devices }}
            {% else %}
              "error": "No devices found or invalid data"
            {% endif %}

And finally the config file for the flex-card-table

type: custom:flex-table-card
title: Fing Devices
entities:
  - sensor.fing_devices_table
sort_by: x.name+
columns:
  - name: Name
    data: devices
    modify: >-
      if (x.name && x.name.length > 0) {'<div style="font-weight: bold;">' +
      x.name +'</div>'} else {'-'}
    fallback: N/A
  - name: MAC Address
    data: devices
    modify: if (x.mac && x.mac.length > 0) {x.mac} else {'-'}
    fallback: N/A
  - name: IP Address
    data: devices
    modify: if (x.ip[0] && x.ip[0].length > 0) {x.ip[0]} else {'-'}
    fallback: N/A
  - data: devices
    name: " "
    icon: mdi:arrow-up-drop-circle-outline
    align: center
    modify: |-
      var ICON = 'mdi:arrow-up-drop-circle-outline';  
      var ICONCOLOR = 'color:green'; 
      if (x.state =='UP')
        ICON = 'mdi:arrow-up-drop-circle-outline'; 
      if (x.state =='UP')  
        ICONCOLOR = 'color:green';
      if (x.state =='DOWN')   
        ICON = 'mdi:arrow-down-drop-circle-outline'; 
      if (x.state =='DOWN')           
        ICONCOLOR = 'color:red'; 
      '<ha-icon icon="' + ICON + '"  style="' + ICONCOLOR + '">'
    fallback: Unknown
  - name: Type
    data: devices
    modify: |-
      if(x.type == undefined)
      {"-"}
      else {
        switch(x.type){
          case 'DESKTOP':
            '<ha-icon icon="mdi:monitor">'; 
            break;
          case 'GATEWAY':
            '<ha-icon icon="mdi:hub-outline">'; 
            break;
          case 'LAPTOP':
            '<ha-icon icon="mdi:laptop">'; 
            break;
          case 'LIGHT':
            '<ha-icon icon="mdi:lightbulb-outline">'; 
            break;
          case 'LOUDSPEAKER':
            '<ha-icon icon="mdi:speaker-wireless">'; 
            break;
          case 'MEDIA_PLAYER':
            '<ha-icon icon="mdi:play-network-outline">'; 
            break;
          case 'MOBILE':
            '<ha-icon icon="mdi:cellphone">'; 
            break;
          case 'NAS_STORAGE':
            '<ha-icon icon="mdi:nas">'; 
            break;
          case 'PRINTER':
            '<ha-icon icon="mdi:printer">'; 
            break;
          case 'RADIO':
            '<ha-icon icon="mdi:radio">'; 
            break;
          case 'RASPBERRY':
            '<ha-icon icon="mdi:raspberry-pi">'; 
            break;
          case 'ROUTER':
            '<ha-icon icon="mdi:router">'; 
            break;
          case 'SMART_CONTROLLER':
            '<ha-icon icon="mdi:hub">'; 
            break;
          case 'SMART_HOME':
            '<ha-icon icon="mdi:home-outline">'; 
            break;
          case 'SMART_PLUG':
            '<ha-icon icon="mdi:power-plug-outline">'; 
            break;
          case 'STB':
            '<ha-icon icon="mdi:set-top-box">'; 
            break;
          case 'SURVEILLANCE_CAMERA':
            '<ha-icon icon="mdi:cctv">'; 
            break;
          case 'TABLET':
            '<ha-icon icon="mdi:tablet">'; 
            break;
          case 'TELEVISION':
            '<ha-icon icon="mdi:television">'; 
            break;
          case 'VOICE_CONTROL':
            '<ha-icon icon="mdi:account-voice">'; 
            break;
          case 'WIFI':
            '<ha-icon icon="mdi:wifi">'; 
            break;
          case 'WIFI_EXTENDER':
            '<ha-icon icon="mdi:wifi-sync">'; 
            break;
          default:
            '<ha-icon icon="mdi:help-circle-outline">';
          }
       }
    sort_unmodified: true
    align: center
    fallback: N/A
  - name: Make
    data: devices
    modify: if (x.make && x.make.length > 0) {x.make} else {'-'}
    fallback: N/A
  - name: Model
    data: devices
    modify: if (x.model && x.model.length > 0) {x.model} else {'-'}
    fallback: N/A
  - name: First Seen
    data: devices
    modify: |-
      if(x.first_seen == undefined)
      {"-"}
      else {
        var date = new Date(x.first_seen);
        String(date.getDate()).padStart(2,'0')+"/"+
        (String(date.getMonth()+ 1).padStart(2,'0'))+"/"+
        date.getFullYear()+
        " "+
        String(date.getHours()).padStart(2,'0')+":"+
        String(date.getMinutes()).padStart(2,'0')+":"+
        String(date.getSeconds()).padStart(2,'0')
      }    
  - name: Last Changed
    data: devices
    modify: |-
      if(x.last_changed == undefined)
      {"-"}
      else {
        var date = new Date(x.last_changed);
        String(date.getDate()).padStart(2,'0')+"/"+
        (String(date.getMonth()+ 1).padStart(2,'0'))+"/"+
        date.getFullYear()+
        " "+
        String(date.getHours()).padStart(2,'0')+":"+
        String(date.getMinutes()).padStart(2,'0')+":"+
        String(date.getSeconds()).padStart(2,'0')
      }

This gives example output like this:

Very happy that thanks to the contributions in these posts I found ways to substitute icons and format dates etc.

Issues?
I can’t get the initial sort to work. I assumed the x.name would give me that as the default but no joy. The sort on column feature works perfectly though.

I would also love to. able to filter within the table output amd/or search for a record but I believe neither of these is not a feature as yet. In the absence of this I thought I would just add another sensor/template sensor/table config with a value template on the sensor like this:

- platform: rest
  name: fing_api_data_down
  resource: http://192.168.1.xxx:49090/1/devices?auth=fingapiauthorisationcode  # Replace with actual API endpoint
  json_attributes:
    - devices  
  value_template: "{{ value_json.devices | selectattr('state', 'eq', 'DOWN') | list | first }}" 

But that still shows all records. I suspect I just don’t understand the formatting of value templates sufficiently.
Any ideas on a good way to achieve this would be greatly appreciated.

Anyway many thanks for all the ideas from all of you in this post and thanks to @Ildar_Gabdullin for setting it up and all the people who have contributed to this card

3 Likes

(post deleted by author)