WTH can't input helpers be read only in UI

I don’t believe Frenck’s post, that you quoted above, is about entities created by the Template, MQTT, REST, etc integrations. All of those official integrations allow the user to decide, based on their intended application, whether to employ attributes or not.

The advantage the attributes property has over an entity’s state property is that it’s not limited to just a 255-character string; it’s a dictionary whose values can contain other data types with no size limitation (other than the host’s hardware limits).

The comment by Frenck that does apply is the one he posted above:

A read only input helper would be a sensor… not an input.

Based on that opinion, CentralCommand and TheFes offered straightforward ways of employing a Trigger-based Template Sensor whose value can be easily set by a service call, just like for an Input helper. CentralCommand’s example offers the additional advantage of supporting device_class and unit_of_measurement so the value’s appearance in the UI can be influenced (not possible with Input helpers).

I entirely disagree, but this is clearly not the place to discuss this. So I will leave it at that and not drag this WTH any further offtopic.

Just one thing : input helpers do absolutely support unit_of_measurement. I use it all the time.

Yes, you’re correct; Input Number supports unit_of_measurement but not device_class. All other Input helpers support neither (nor icon); a Template Sensor offers far more control over its value’s appearance.

Input number, input text and input select all support icon. I don’t really see the point of UoM on anything other than a number. The lack of device_class and state_class configurability is indeed a shortcoming and could / should be addressed in the future.

Template Sensors also supports all the attributes needed for long term energy measurement so that would be useful for someone who wants to create a “variable” for that purpose.

I suspect it may face the same objection that Frenck posted earlier.

? I didn’t. There’s no attributes at all in here:

I mentioned if you wanted attributes you could do that too since I know the variables component supports that. But this entity just has a state.

For reference I have nothing wrong with fes’s solution. It’s a pretty creative way to make it so you can easily create and manage variables on the fly with no changes to yaml. But if you don’t like attributes you don’t need them.

Right, I was mostly referring to fes’s code. Yours is certainly a better approach imo. But in the end it all boils down to the same point: all that should not even be necessary. All these are workarounds. They’re reimplementing the concept of a variable with unneeded overhead and boilerplate code. All that should be abstracted away in an easy, intuitive and simple to use way over the UI, in the HA core. As far as I see it, input_* are almost there, at least for the common use cases.

The biggest advantage of my code is that it allows the user to store something without the need to create a helper.
However, it was a bit offtopic here. I replied here, because the code of @CentralCommand was here, but my code can not be used as a solution for the WTH

However, if at any point while creating an automation or script you think, oh, I need to store this data, you don’t have to go to the helper section to create a helper, you can just create the event. And after the data is used (by another automations or script or whatever) you can remove it again.

1 Like

Publishing the value to my mqtt broker and using an mqtt senor used to be my go-to for this.

Regarding the timestamps:

variables:
  test:
    value: 20
    timestamp: 2022-10-01 21:54:01.944622+02:00
  test2:
    value: hello
    timestamp: 2022-10-01 20:51:01.933622+02:00

This would still work relatively easy with state_attr('sensor.variables', 'variables').test.value

3 Likes

Yep, that’s what I do and I have an automation that discovers it on startup (and when an event is fired creating it).

@123
Here you go, an updated version.
You can set a default setting if you want to store the timestamp or not in the default_timestamp attribute of the template sensor, but you can also overrule that setting using set_timestamp: true or set_timestamp: false in the event data

Updated code for the template sensor:

template:
  - trigger:
      - platform: event
        event_type: set_variable
      - platform: event
        event_type: remove_variable
    sensor:
      - unique_id: 4a4c8e53-9e68-4198-9cc5-b336e228ea4d
        name: Variables
        state: Variables
        attributes:
          default_timestamp: true
          variables: >
            {% set current = this.attributes.get('variables', {}) %}
            {% if trigger.event.event_type == 'set_variable' %}
              {% if trigger.event.data.get('set_timestamp', this.attributes.get('default_timestamp', false)) %}
                {% set new = {trigger.event.data.key: {'value': trigger.event.data.value, 'timestamp': now().isoformat()}} %}
              {% else %}
                {% set new = {trigger.event.data.key: trigger.event.data.value} %}
              {% endif %}
              {{ dict(current, **new) }}
            {% elif trigger.event.event_type == 'remove_variable' %}
              {{ dict(current.items() | rejectattr('0', 'eq', trigger.event.data.key)) }}
            {% endif %}

To add a variable (in an automation or script):

action: # for script this is sequence:
  - event: set_variable
    event_data: 
      key: some_test
      value: Some Value
      set_timestamp: false # optional, by default the setting in the sensor will be used, if not added there, the default is false

Result (attributes of sensor.variables):

default_timestamp: true
variables:
  test_without_timestamp: What time is it?
  test_with_timestamp:
    value: I know the time!
    timestamp: '2022-10-02T20:19:41.713241+02:00'
friendly_name: Variables

To remove a variable (in an automation or script):

action: # for script this is sequence:
  - event: remove_variable
    event_data: 
      key: some_test

To get a variable out of the sensor you will have to use templates:

# with timestamp
some_key: "{{ state_attr('sensor.variables', 'variables').variable_key.value }}"

# without timestamp
some_key: "{{ state_attr('sensor.variables', 'variables').variable_key }}"
17 Likes

Is the solution by TheFes still supposed to work or were there any updates that made it out-of-date?

I’m trying to get it working but, of course, without success. How can I see what is in sensor.variables so I can debug it further?

I’m trying to set the value like this:

        sequence:
          - event: set_variable
            event_data:
              key: myKey
              value: 1

and read/use it like this:

  - condition: template
    value_template: >-
      "{{ (state_attr('sensor.variables', 'variables').myKey|int) != 1 }}"

Where’s the failure?

Go to Developer Tools > States, find sensor.variables in the list, view what’s displayed in the Attributes column.

1 Like

How do you use this dictionary in node-red flow?

It’s been a couple of weeks so I assume you solved this. Looking at your quote above the " marks shouldn’t be there because of the >-.

  - condition: template
    value_template: >-
      {{ (state_attr('sensor.variables', 'variables').myKey|int) != 1 }}

Posting this for future eyes.

Old thread but it is still relevant :smiling_face:
I made a small extension to the nice version of @TheFes (thanks for sharing your variable sensor).
The version shared below, just adds:

  • event clear_variables to remove all variables
  • logging to the logbook
  • apply defaults in case value inputs are missing

- trigger:
    - platform: event
      event_type: set_variable
    - platform: event
      event_type: remove_variable
    - platform: event
      event_type: clear_variables
  action:
    - service: logbook.log
      data:
        name: "{{trigger.event.event_type}}:"
        message: >
          {{ trigger.event.data.key | default('-') }}
          {%- if trigger.event.event_type == 'set_variable' -%}
          : {{ trigger.event.data.value | default('not defined!')}}.
          {%- endif -%}
  sensor:
    - unique_id: 4a4c8e53-9e68-4198-9cc5-b336e228ea4d
      name: Variables
      state: Variables
      attributes:
        default_timestamp: true
        variables: >
            {% set current = this.attributes.get('variables', {}) %}
            {% if trigger.event.event_type == 'set_variable' %}
              {% if trigger.event.data.get('set_timestamp', this.attributes.get('default_timestamp', false)) %}
                {% set new = {trigger.event.data.key: {'value': trigger.event.data.value | default('not defined!'), 'timestamp': now().isoformat()}} %}
              {% else %}
                {% set new = {trigger.event.data.key: trigger.event.data.value | default('not defined!')} %}
              {% endif %}
              {{ dict(current, **new) }}
            {% elif trigger.event.event_type == 'remove_variable' %}
              {{ dict(current.items() | rejectattr('0', 'eq', trigger.event.data.key)) }}
            {% elif trigger.event.event_type == 'clear_variables' %}
              {{ {} }}
            {% endif %}

1 Like

Then there’s this.

1 Like