custom-cards / flex-table-card

Highly Flexible Lovelace Card - arbitrary contents/columns/rows, regex matched, perfect to show appdaemon created content and anything breaking out of the entity_id + attributes concept
GNU General Public License v3.0
198 stars 23 forks source link

Question/Feature request: joining entity attributes in one column? #19

Closed pejotigrek closed 4 years ago

pejotigrek commented 4 years ago

I'm playing with your custom card to make a table that lists some part of my devices, checking it's state, last_changed date & time etc. and because I want to put it in popup window, in lovelace ui that's designed for phone screens, I have kinda limited space ;)

so right now it's easy to have a regular table, one attribute for a column:

Device State Change EntityID
friendly name online 2020-02-01, 17:00 device.some_name

and I can of course change color of state text based on it's,, state :) so I have green online and red-background, white-letters offline.

now things are getting tricky, as I wanted to do something like this:

Device Change
friendly name / device.some_name 2020-02-01, 17:00

where device.some_name is displayed as a secondary line below the friendly_name and additionaly if device is offline, I'd like to change the friendly_name's color to red.

while I know how to concatenate or replace strings to have two lines of text, or to change some color, I'm having troubles with figuring out how to configure column so I can put name and entity_id together in one.

first I thought that attr_as_list is what I'm looking for but either I can't understand the proper usage, or it isn't that.

ok, so after this long introduction:

phew, I'm only hoping, that I didn't complicate the above post/question - english isn't my first language and while I'm 100% sure what I wanted to say/wrote, I'm not that sure I correctly wrote that down ;)

daringer commented 4 years ago

This is more or less why attr_as_list does exist, allowing to put arbitrary content into each row and cell. Basically I do this using the custom-component variables: https://github.com/snarky-snark/home-assistant-variables, which is filled with the appropriate data by appdaemon using the service provided by the variable component.

Overall the idea was to not get too complex on frontend side, usually I do prefer data-manipulation/assembly to be not part of the frontend. Therefore attr_as_list was invented to allow an arbitrary backend to assemble some data, fill it into some variable's attributes and then simply show it.

Nonetheless, questionable would be "how much data-manipulation on frontend side is too much?". Tricky one, e.g., #21 and #20 are also requesting a similar (merging or css-on-content-condition) feature. Maybe already allowing merging of columns, like this: merge_col: <column-id> might already solve most use-cases.

Further "conditional css-properties based on cell-contents" is quite ugly to generalize, but one could merge the Device and State column and then use modify: to run js-code in order to change the color of the cell, but merge_col is for sure necessary before that ...

daringer commented 4 years ago

well, merge_col is maybe the wrong approach, how about the possibility to select multiple attributes/properties into a single cell, likely far better than merging it afterwards. Will have to think about the col-config to enable that, currently not so obvious... let's see...

daringer commented 4 years ago

Using this approach here, you should get multiple cells "merged" or selected together.

type: 'custom:flex-table-card'
title: Power Consumption (Top 10)
columns:
  - attr: node_id
    name: NodeID
  - attr: power_consumption
    name: Power
  - name: Energy
    prop: state
  - name: My Multi-Item-Field
    multi_delimiter: ','
    multi:
      - - attr
        - node_id
      - - attr
        - power_consumption
      - [prop, state]               /* <-- means the same, but looks prettier... */
entities:
  include: 'sensor.*_energy(_[0-9]+)?'

multi just serves as a list of multiple entries for data selection. Due to backwards-compatibility I have to do this with a separate multi config property. The selected cell contents are acquired and finally get concatenated using the delimiter ' ' (whitespace) by default. You might change this default behavior by setting multi_delimiter to an arbitrary string.

Once you have this, it's quite straight forward to color cells based on their content. Just use something similar to #20 using modify. You might insert a <span> and set the cell-css-class like this:

modify: '(x.split(",")[0] == "some condition") ? 
    x.split(",")[1] : 
    `<span class=font_color_blue>${x.split(",")[1]}</span>`'

Of course, you might also just set the css-style via js directly, whatever serves. But yes, we are reaching a point, where one might question whether this is really the correct approach for doing something like this.

Anyways, hope it works, cheers

pejotigrek commented 4 years ago

whoa! thanks for such a long and informative answers! I surely must take a look on that with clear mind to understand everything correctly & try to implement in my scenario. I hope it won't take too much time for me and I'll be able to report here if I succeed or not ;)

on a side not - about the variables component - I use another custom component for variables support, but I get the idea. "problem" is that I'm also trying not to duplicate any entities, and since few weeks I'm trying to phase out that component. meaning: if there's an entity with some attribute, I am not too happy to create another entity that would just keep the old entity's attribute, if you know what I mean ;) sure, if there's no other solution and everything else fails I'll bite the bullet, but it's not a desired solution [for me ofcourse].

daringer commented 4 years ago

if there's an entity with some attribute, I am not too happy to create another entity that would just keep the old entity's attribute, if you know what I mean ;)

I totally know what you mean, especially if you handle more than a handful of devices within your smart home, any double book-keeping is painful and error-prone. But I think flex-table-card should now give you capabilities to realize your initial ideas - post a screenshot on success if possible.

pejotigrek commented 4 years ago

ok, I can tell that last card update works great!

code/design is still under construction, but first draft looks very promising :)

I'm using this code:

- type: custom:flex-table-card
  sort_by: friendly_name+
  clickable: true
  css:
    table+: "border: 0; border-collapse: collapse;"
    table: "font-size: 0.85em;"
    'tbody tr:nth-child(even)': "background-color: transparent;"
    'tbody tr td': "padding-top: 2px; padding-bottom: 2px; border-top: 1px dotted var(--primary-text-color);"
  entities:
    include:
    - device_tracker.png_soa*
    - device_tracker.png_esp*
  columns:
    - name: Urządzenie
      icon: mdi:power-settings
      multi_delimiter: "|"
      multi:
        - [attr, state]
        - [attr, friendly_name]
      modify: '((x.split("|")[0] == "online") ? `<span style="color:#138535;">${x.split("|")[1]}</span>` : `<span style="color:#FFF;background:#F00;">${x.split("|")[1]}</span>`)'
    - prop: last_changed
      icon: mdi:calendar-check
      modify: (x.split("T")[0].concat(", ")).concat(x.split("T")[1].split(".")[0][0]).concat(x.split("T")[1].split(".")[0][1]).concat(x.split("T")[1].split(".")[0][2]).concat(x.split("T")[1].split(".")[0][3]).concat(x.split("T")[1].split(".")[0][4])
    - prop: entity_id
      icon: mdi:textbox
      modify: x.replace("device_tracker.", "")
      prefix: "<span style='font-style: oblique; color: var(--secondary-text-color)'>"
      suffix: "</span>"

to get this effect: image

above table shows my ESP-based devices [sockets, switches etc.] and their online/offline status, status last changed time and entity id for easy config search. this is kinda system/debug cart that I use from time to time when looking for issues [which device went offline/has connection problems etc.], so it meant to be technical :)

thanks for update and all the hints!

pejotigrek commented 4 years ago

hmmm, I've noticed that there's some isssu with sorting - it isn't sorted by friendly_name as configured in the code. did I missed something?

daringer commented 4 years ago

This looks very promising, nice collection of ESP devices :nerd_face:

Concerning the sort_by issue. The to-be-sorted column is identified using the provided sort_by value, in this case friendly_name+. Under the hood any matching value from id, attr, prop, attr_as_list is considered enough.

Keep in mind that sorting is based on final cell contents (excl. prefix, suffix), after modify evaluated, so consider including a hidden column, if you struggle with the sorting.

Your columns names/ids are:

So none of them is matching with friendly_name+, try to simply set id: friendly_name for the column you would like to sort, should do the job...

cheers

pejotigrek commented 4 years ago

thanks! now, seeing that collection & keeping in mind that I'll have about 2-3 tables more, it is certain why I wasn't too happy to add some variable entities with attributes haha ;)

regarding the sorting issue - I get it! didn't thought about this. I'll just add a hidden column then. no problem :)

pejotigrek commented 4 years ago

ok, I can confirm, adding and hiding another column was enough :)

after adding friendly_name as first column, I also added this line to css:

'thead tr th:first-child, tbody tr td:first-child': "display: none;"

and everything looks like it meant to :)

so for now - my problem is solved, I'm closing it :) thank you!

daringer commented 4 years ago

Always good to hear, you're very welcome... Apart from that, hiding columns is out-of-the-box possible using the hidden: true configuration property within one column's sub-config. (This is a bad moment for the documentation, I know much to do there) cheers

pejotigrek commented 4 years ago

hah! I had my yaml-momentum going and didn't even thought that hiding columns will be in the docs.. cool, I'll exchange my css to native solution :) thanks again :)

pejotigrek commented 4 years ago

@daringer I don't want yet to reopen this, or make another one topic, but suddenly the "main" line:

modify: '((x.split(",")[0] == "online") ? `<span style="color:#138535;">${x.split("|")[1]}</span>` : `<span style="color:#FFF;background:#F00;">${x.split("|")[1]}</span>`)'

is giving me "undefined", while [attr, state] and [attr, friendly_name] are outputting proper data.

component installed: version 13960ca

daringer commented 4 years ago

This very commit: 13960ca is when "strict" mode is active for the custom-card at least. You could first try to confirm this reason by simply setting type: module back to type: js within your lovelace config. If you don't see the undefined case popping up anymore you've found the reason.

JS-strict mode will not allow all the dirty js things the internet has taught us. I would suggest setting some example "x"s within the chromium console and simply execute the modify line.

pejotigrek commented 4 years ago

~~unfortunately bringing back type: js didn't helped :( where can I change this "strict" setting?~~

nevermind. there must have been some syntax error in the troubled line. I've made a copy-and-paste fix using another table which displays other devices in the very same way - and it started working. don't know how did it happened, as the first line was a source of every other table, but now it's fixed. sorry for bothering you ;(