How To Get Template Entity Values To Persist Through Restart

I’ve created a custom entity and it works well except that the state and attributes do not persist through a restart. I thought it was because I was setting the values via templating but even when I remove those it always resets the device entirely (and if I take out templating then I lose the tilt attribute).

How can I get the state of my curtains to persist through a restart?

      curtains_side:
        unique_id: "curtains_side"
        device_class: curtain
        friendly_name: "Side Curtains"
        position_template: 100
        tilt_template: 0
        
        open_cover:
          service: pyscript.curtain_control_open
          data:
            entity_id: cover.curtains_side
            
        close_cover:
          service: pyscript.curtain_control_close
          data:
            entity_id: cover.curtains_side
            
        stop_cover:
          service: cover.stop_cover
          entity_id: cover.curtains_side
          
        set_cover_position:
          service: cover.set_cover_position
          data:
            position: "{{position}}"
            
        set_cover_tilt_position:
          service: cover.set_cover_tilt_position
          data:
            position: "{{tilt}}"

A Template entity has its state evaluated on startup. In the case of the Template Cover you have defined, its state is determined by position_template. In other words, on startup it evaluates the template assigned to position_template and the result of it will be its state. In the example you posted, it will always be 100.

Thank you for the reply, but I tried with no template and had the same issue.

Not following you. Why would you not assign a template to an option (position_template) that clearly needs one?

What are the reported values at restart?

I’m relatively new to HASS so this is all a learning curve. I assumed that I do need to have a position template, which is the reason for the question - when I assign the above templates it always resets to position 100, which I understand it resets to 100 on restart since that is the default value. That caused me to try removing the template to see if that was the reason why it would not persist after a reload.

I believe what I’m not clear on is what the template needs to be in order to make sure that when I restart HASS that whatever the device setting was before the restart is retained after the restart.

Given the above device template, it always starts up as position at 100, which doesn’t surprise me since that seems to be logical, I just don’t know what kind of formula I would put there.

This is my first custom entity of any use (I’ve created some sandbox and play devices just to learn), so I’m not quite sure how all the pieces work together just yet.

What are you using to get the feedback for the cover position/tilt?

Sorry if I sound like a dolt, I don’t understand the question. The way the device is set up I have it calling a pyscript to perform the actions because it ties into other devices, so that script opens/closes/tilts accordingly and then updates the attributes of the device when done. The correct values are reflected in the dashboard, I can see everything works exactly as expected, it opens and closes when asked and the icon/status of the dashboard element reflects that.

So it starts out at 100 open (my issue), I then close it and it works as expected, restart HASS and it’s open again even though it was closed when restarted.

Not sure if that makes it harder to understand or easier :).

can you post a screenshot of the entity in the developers tools-> states page showing the state and attributes of the cover entity after you have moved it to a position and it is reflected there correctly and before you restart HA?

Do you mean that the cover is actually open or is it actually closed but it only shows open incorrectly?

Yes, the cover is actually closed, but restart sets it to open.

States when open (copied from developer):

current_position: 100
current_tilt_position: 0
friendly_name: Side Curtains
supported_features: 255
device_class: curtain

Then I close the drapes via my dashboard, the new attributes:

current_position: 0
current_tilt_position: 100
friendly_name: Side Curtains
supported_features: 255
device_class: curtain

OK, then try this and see if it works:

curtains_side:
  unique_id: "curtains_side"
  device_class: curtain
  friendly_name: "Side Curtains"
  position_template: "{{ state_attr('cover.curtains_side', 'current_position') }}"
  tilt_template: "{{ state_attr('cover.curtains_side', 'current_tilt_position') }}"
.
.
.

Thank you, that’s the kind of stuff I’m still not sure about doing! I get that it’s all Python encapsulated in brackets but I don’t yet have a firm grasp on all that I can do using that type of encapsulated structure. However, it didn’t resolve the issue. I made the changes and restarted HASS, the curtain was open, I then closed the curtain and restarted HASS again, they came back as open again.

My template:

      curtains_side:
        unique_id: "curtains_side"
        device_class: curtain
        friendly_name: "Side Curtains"
        position_template: "{{ state_attr('cover.curtains_side', 'current_position') }}"
        tilt_template: "{{ state_attr('cover.curtains_side', 'current_tilt_position') }}"

The actual opening and closing is all working perfectly, but the attributes don’t persist.

After closing the curtains and before restarting:

current_position: 0
current_tilt_position: 100
friendly_name: Side Curtains
supported_features: 255
device_class: curtain

Right after the restart completes:

friendly_name: Side Curtains
supported_features: 255
device_class: curtain

So it seems like the position and tilt, when starting up, don’t even set the attributes when it has the scripting as templates.

There’s the important clue. Your pyscript is setting the attributes of cover.curtains_side.

In other words, your pyscript is setting values that only exist in Home Assistant’s state machine and disappear after a restart. It’s no different than using the popular set_state.py python_script (developed by rodpayne).

The technique you’re using will never produce values that survive a restart. Upon startup, the template Cover will always initialize itself to whatever it finds in position_template which, according to your example, will be 100.

That makes sense. So when you write the attributes back it’s only in runtime, not persistent, but if you use Hass.states.set then it persists?

No. If you look at the last line in rodpayne’s python_script, that’s precisely the call he uses to set the entity’s state (and it only lasts until the next restart).

    hass.states.set(inputEntity, inputState, inputAttributesObject)

The only way to make this work the way you want is to make your pyscript write its position values to a sensor entity and make position_template refer to that sensor:

  position_template: "{{ states('sensor.curtain_position') }}"

However, the trick is you need a sensor whose value you can set and will survive a restart (otherwise, we are back to square one, setting values to the state machine that fail to survive a restart). Clearly, we can’t do that with a Template Sensor but we can do it with an MQTT Sensor.

The value must be published using the retain flag to that it is stored on the MQTT Broker. On startup, the MQTT Sensor subscribes to its topic on the broker and acquires the stored value (meaning the last recorded position of the curtains).

Let me know if you need help to implement it.

I think I understand. So, at least in this scenario (maybe all scenarios), any attribute update is not persistent unless it is redefined as a MQTT sensor. Equating to needing to have a sensor defined for the sole purpose of storing persistent values. This begs the question: can a single custom MQTT sensor have custom attributes where I could create sort of a “global” sensor for all the various things I do that I might want persistent attributes retained for or do I need one sensor per setting I want to persist?

Yes. Check the documentation for the MQTT Sensor integration and you’ll find json_attributes_template.

Basically, if the payload is published as a JSON dictionary, the template (assigned to json_attributes_template) is used to parse the payload and assign its values to separate attributes.

However, each payload must contain all attributes. In other words, you can’t send one payload containing values for one attribute, followed by another payload containing a value for some other attribute. Each payload must contain everything (so it may be repeating unchanged values for some attributes). If you don’t do this, only the attribute it receives will exist (and whatever others it may have had will disappear).


Here’s an example that I have used in the past. However, I caution you that it may no longer work due to the recent implementation of native types (which have proven to make some tasks more complicated).

The comment shows a sample JSON payload that you would publish to an MQTT topic called test. The value_template extracts cpu_load and makes that the sensor’s state. The json_attributes_template extracts the other values and creates associated attributes.

# Test accessing attributes with spaces in their names
# Designed to receive a JSON payload like this:
# {"used_space": 25, "sys_clock_speed": 1500, "cpu_temp": 43.0, "voltage": 0.8500, "cpu_load": 1.25, "memory": "False", "swap": "False"}
  - platform: mqtt
    name: "pi monitor"
    state_topic: "test"
    value_template: "{{ value_json.cpu_load }}"
    json_attributes_topic: "test"
    json_attributes_template: >-
      { "used space": {{value_json.used_space}},
        "sys clock speed": {{value_json.sys_clock_speed}},
        "cpu temp": {{value_json.cpu_temp}},
        "voltage": {{value_json.voltage}},
        "memory": "{{value_json.memory}}",
        "swap": "{{value_json.swap}}" } 

Feel free to experiment with it. However, keep in mind that this example worked before support for native types was introduced so it may now complain about the format of the JSON string used for json_attributes_template.

1 Like

Thank you for all that info! Let me play around and see if I can figure it out and if I run into trouble I’ll post back.