custom-cards / button-card

❇️ Lovelace button-card for home assistant
MIT License
1.97k stars 242 forks source link

Make variables accessible to each other #656

Closed adabelleleiram closed 1 year ago

adabelleleiram commented 1 year ago

Is your feature request related to a problem? Please describe. I often find myself in situations where I'd like to use variables in the javascript of other variables, but the variables object isn't defined.

Describe the solution you'd like I'd like the variables to be accessible when defining other variables. A way to avoid e.g. circular dependencies could be to define them in the order they exist in yaml. That can work across templates as well. E.g. (sorry about the formatting, couldn't get it to work with code formatting so tried bullets)

Describe alternatives you've considered I've tried hacking it anyway, e.g. by using other variables like name, icon etc as well as using yaml anchors. It just makes it very confusing.

Additional context

luixal commented 1 year ago

Just got here looking for the same thing. My idea is a bit more simple, but goes the same way and this will cover it. I really just need to be able to define variables with the value of the previous variables. I'll put a real example below.

According to the initial issue, I'm putting the formatted example here:

# FIRST TEMPLATE CARD
custom_card_a:
variables:
  variable_a: 2
  variable_b: >
    [[[ 
      // can access variable_a but not variable_c
      return variables.variable_a * 2;
    ]]]
  variable_c: 3

# SECOND TEMPLATE CARD (templated by custom_card_a)
custom_card_b:
  template: custom_card_a
  variables:
    variable_d: >
      [[[
        // can access all variables defined in custom_card_a as template was assigned before variables in custom_card_b
        return variables.variable_c * 3;
      ]]]

# THIRD TEMPLATE CARD (templated by custom_card_b)
custom_card_c:
  template: custom_card_b
  variables:
    variable_e: >
      [[[
        // cannot access any other variables as template was assigned after variables in custom_card_c
        return 4;
      ]]]

I haven't thought about re-using variables in other templates, but being able to define variables using previous variables is really useful. This is my use case right now:

I'm building a dashboard for my irrigation system. My irrigation system is controlled by the irrigation-unlimited integration. This integration exposes entities for irrigation zones and irrigation controller. Sequences info is stored in and attribute of the irrigation controller. That attribute has a bunch of nested info.

So, when building a button for showing/controlling sequences, I'm using a button-card template with a variable for the sequence index, this way:

button_card_templates:
  irrigation_button_sequence:
    variables:
      index: 1 #<----- NOTE THIS
    show_icon: false
    show_name: false
    custom_fields:
      main_card:
        card:
          type: custom:mushroom-template-card
          entity: '[[[ return entity.entity_id ]]]'
          primary: '[[[ return `Riego ${entity.attributes.sequence_status[variables.index].name}` ]]]' #<-- AND THIS
          secondary: ...
    ...

So, I'm having entity.attributes.sequence_status[variables.index].something everywhere. Too much repetition and it also means that for every field I make a new access to the object field just to get the same info once and again.

It would be great if I could just do this:

button_card_templates:
  irrigation_button_sequence:
    variables:
      index: 1 #<----- NOTE THIS
      sequence: entity.attributes.sequence_status[variables.index]
    show_icon: false
    show_name: false
    custom_fields:
      main_card:
        card:
          type: custom:mushroom-template-card
          entity: '[[[ return entity.entity_id ]]]'
          primary: '[[[ return `Riego ${variables.sequence.name}` ]]]' #<-- AND THIS
          secondary: ...
    ...

Another useful thing would be having variables store functions for reuse them ;)

Thanks!

RomRider commented 1 year ago

The issue with variables depending between them is that they would need to be declared in a certain order which we can't control. Eg if A depends on B, I'd need to know that I need to evaluate the B template before A, however, at the moment, it would evaluate A before B (alphabetical order)

luixal commented 1 year ago

Umm... I guess providing variables as an array would solve that, but retrocompatibility would have to be handled.

Anyhow, that wouldn't be a "no go" for me. Having the alphabetical order info well documented would just be enough.

RomRider commented 1 year ago

Yes, variable as an array, but then I'd have to handle duplicates and make the card fail somehow... And also what about merging config templates? It gets really complex quite fast...

Another useful thing would be having variables store functions for reuse them ;)

You can already do that, just return a function and you can reuse it in another template (but not in another variable, at least until I find a convenient way...)

variables:
  addOne: '[[[ return function addOne(input) { return input + 1;}; ]]]'
name: '[[[ return variables.addOne(2); ]]]' # will display 3 as the name
luixal commented 1 year ago

Would something like this solve the problem?

variables:
      index: 1
      factor: 12
      sequence: 
          depends:
              - index
              - factor
          value: index * factor

Not sure if it's too verbose or it's even better for readibility 🤣

RomRider commented 1 year ago

OMG, then I would even have to manage circular dependencies. 😨 I'll release something simple for now: ASCII order dependency on the variable name

github-actions[bot] commented 1 year ago

:tada: This issue has been resolved in version 4.0.0-dev.17 :tada:

The release is available on GitHub release

Your semantic-release bot :package::rocket:

RomRider commented 1 year ago

Please test and report back 😄

github-actions[bot] commented 1 year ago

:tada: This issue has been resolved in version 4.0.0 :tada:

The release is available on GitHub release

Your semantic-release bot :package::rocket: