Evohome all in one screen architecture guidance

Hi folks,

I am quickly finding that what I want to do in HA needs the use of code. For context about me, I am not a programmer by profession, but I have for instance created a custom types in PowerShell exposing the native C interface of a Windows dll so that I can call those functions. Advapi32.dl LsaEnumerateAccountsWithUserRight for instance. But that is absolutely the limit of the sort of thing I am capable of. Mostly I have written fairly small self-contained scripts for work [1000 lines is normal] in PowerShell using .net runtime natively [not powershell commandletts “too slow”]. Some scripts might have a few . sourced sections to make the editing manageable and modular. Because in a Powershell script everything is global or local in a function scopes are quite simple to deal with. Mapping my level into HA is confusing.

I am struggling [and here is the rub as always in technology] to know what I don’t know, so please bear with as I am not clear WHAT questions I should be asking to get me to the answers I need. I am not clear whether I should be creating template helpers [other helpers], automations, functional UI code in lovelace – etc. or, just be writing my own custom components entirely from scratch and am worried about investing time in the wrong direction.

My problem centres [I think] on how things interact with each other, I don’t mean devices, I mean architectural elements of the code I produce in the HA system. Where are globals, locals, array’s, boolean’s all specified, how do they work. Do we do this in the UI code itself or write automations that can be triggered from the UI? Specify static functions in the config yaml. What are the helpers for, I am using these to expose elements that I can glue together with code elsewhere but is that “right” ?? Not sure.

It is confusing because it’s like a ginormous leggo set with no pictures. The way these things are stuck together isn’t the focus of the documentation that I have found so far. The developer documentation guidance for HA explicitly prohibits the use of example code beyond the most basic required artefacts for the integration, so that code can be copied and pasted and I understand why. I need to get into the level above the basic but below development and I am not asking the right questions to hit those resources on my own. I need information on how to structure my approach in the “right way”. Info on different approaches would be good too !
What I am worried about is misusing the functional architecture and ”doing it wrong” and then find out my way is being “deprecated” or is glitchy or buggy / slow at a later stage of development “coz I done it wrong”.

So I am asking for the charity of the community to reach down a hand into the newbie pit and hold up an arrow for me to follow please.

If you’ve read this far, thank you. Because the above is a broad ask, the below is cobbled together in a few hours to try to understand the system [unsuccessfully] to work out what is going on.
It all focuses around the climate domain and here is one entity which has been deconstructed from the way climate presents it, into what I need via helper templates at the bottom.

This is the YAML for that UI above:

type: vertical-stack
cards:
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: toggle
    name: Previous Temperature Value
    show_state: true
    entity: sensor.back_door_prv_temp
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: toggle
    name: Previous Temperature Time
    show_state: true
    entity: sensor.back_door_this_temp_time
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: none
    hold_action:
      action: none
    entity: sensor.back_door_mode
    show_state: true
    name: Mode
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: none
    hold_action:
      action: none
    entity: sensor.back_door_now_temp_value
    show_state: true
    name: OverRide Temp
  - type: custom:slider-entity-row
    entity: climate.back_door
    max: 21
    min: 5
    step: 0.5
    toggle: false
    hide_state: false
    hide_when_off: false
    full_row: false
    show_icon: false
    name: Over-Ride Temp
  - type: custom:time-picker-card
    entity: input_datetime.evohome_override_time
    initial: "{{ now().strftime('%H:%M') }}"
    name: Override Until
    Hour_mode: 24
    tap_action: none
    hour_mode: 24
    layout:
      embedded: true
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: none
    hold_action:
      action: none
    entity: sensor.back_door_temp_until_time
    show_state: true
    name: OverRide Until
  - show_name: true
    show_icon: false
    name: Clear Override
    type: button
    tap_action:
      action: perform-action
      perform_action: evohome.clear_zone_override
      target: {}
      data:
        entity_id: climate.back_door
    hold_action:
      action: perform-action
      perform_action: evohome.clear_zone_override
      target: {}
      data:
        entity_id: climate.back_door
  - show_name: true
    show_icon: false
    show_state: false
    entity: sensor.back_door_temperature
    type: button
    tap_action:
      action: perform-action
      perform_action: evohome.set_zone_override
      target: {}
      data:
        entity_id: climate.back_door
        setpoint: 12 # should be read from custom:slider-entity-row
        duration: 578 # should be read from custom:time-picker-card
    name: Set Override
    hold_action:
      action: none
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: toggle
    name: Next Temperature Time
    show_state: true
    entity: sensor.back_door_next_temp_time
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: toggle
    name: Next Temperature Value
    show_state: true
    entity: sensor.back_door_nxt_temperature

and these are the helpers which enable those distinct data pieces to be retrieved from the climate domain for the entity.

      {"created_at":"2025-03-01T08:26:11.447395+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JN8D07KQXJ5XEPD07E1FHH4P","minor_version":1,"modified_at":"2025-03-01T10:18:50.934716+00:00","options":{"device_class":"temperature","name":"Back Door Temperature","state":"{{ state_attr('climate.back_door', 'current_temperature') | float }}","state_class":"measurement","template_type":"sensor","unit_of_measurement":"°C"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back Door Temperature","unique_id":null,"version":1},
      {"created_at":"2025-03-02T20:27:37.645518+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNC8NY9D86G93S018PMZRVW7","minor_version":1,"modified_at":"2025-03-02T20:27:37.645521+00:00","options":{"name":"Back_Door_mode","state":"{{ state_attr('climate.back_door', 'status').setpoint_status.setpoint_mode}}","template_type":"sensor"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_Door_mode","unique_id":null,"version":1},
      {"created_at":"2025-03-02T20:38:35.472723+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNC9A0PGG29DFQC3AVEV23FW","minor_version":1,"modified_at":"2025-03-02T20:51:51.902301+00:00","options":{"device_class":"temperature","name":"Back_door_prv_temp","state":"{{ state_attr('climate.back_door', 'status').setpoints.this_sp_temp }}","template_type":"sensor","unit_of_measurement":"°C"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_door_prv_temp","unique_id":null,"version":1},
      {"created_at":"2025-03-02T20:52:57.893710+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNCA4AX5ZRX5N0C7XNWF9RK5","minor_version":1,"modified_at":"2025-03-02T20:52:57.893713+00:00","options":{"device_class":"temperature","name":"Back_Door_nxt_temperature","state":"{{state_attr('climate.back_door', 'status').setpoints.next_sp_temp}}","template_type":"sensor","unit_of_measurement":"°C"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_Door_nxt_temperature","unique_id":null,"version":1},
      {"created_at":"2025-03-02T20:54:29.197719+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNCA742DS9SQPCH6AXZYMHFX","minor_version":1,"modified_at":"2025-03-02T20:54:52.741944+00:00","options":{"name":"Back_door_Next_Temp_time","state":"{{ as_timestamp(state_attr('climate.back_door', 'status').setpoints.next_sp_from) | timestamp_custom('%I:%M:%S %p') }}","template_type":"sensor"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_door_Next_Temp_time","unique_id":null,"version":1},
      {"created_at":"2025-03-02T20:56:28.413197+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNCAARFX18TQ2D79H14DX7Q1","minor_version":1,"modified_at":"2025-03-02T20:56:28.413199+00:00","options":{"name":"Back_door_This_Temp_time","state":"{{ as_timestamp(state_attr('climate.back_door', 'status').setpoints.this_sp_from) | timestamp_custom('%I:%M:%S %p') }}","template_type":"sensor"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_door_This_Temp_time","unique_id":null,"version":1},
      {"created_at":"2025-03-02T21:02:33.100059+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNCANWMCQ5VPGFG4M3J5NFJ4","minor_version":1,"modified_at":"2025-03-02T21:02:33.100061+00:00","options":{"name":"Back_door_temp_until_time","state":"{{ as_timestamp(state_attr('climate.back_door', 'status').setpoint_status.until) | timestamp_custom('%I:%M:%S %p') }}","template_type":"sensor"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_door_temp_until_time","unique_id":null,"version":1},
      {"created_at":"2025-03-02T21:04:13.292654+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01JNCARYFCDWJAEJ3FW4TVS3CA","minor_version":1,"modified_at":"2025-03-02T22:43:16.828140+00:00","options":{"device_class":"temperature","name":"Back_door_now_temp_value","state":"{{ state_attr('climate.back_door', 'status').setpoint_status.target_heat_temperature | float }}","template_type":"sensor","unit_of_measurement":"°C"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","title":"Back_door_now_temp_value","unique_id":null,"version":1}

The thing I am worried about is that doing it this way I will need 163 helpers – just for this and this is just the climate, never mind the 35 smart plugs lights etc.
Surely this is not he right way, is it ?
Mabbe I should be developing a custom card and then I can just re-use that ?

Secondly, which goes back to my initial paragraphs:

HOW – do I start declaring variables and referencing these, passing in arguments to other functions, you know normal programming, in HA, this is what I am missing.
Otherwise my UI code is going to end up enormous as opposed to a few functions I can re-use with pointers to structures and data types of interest.
If someone points to the basic documentation to answer this, I am going to die of embarrassment !

Thanks.

That was a an extremely long read that said basically nothing.

What are you actually trying to do?

What is the actual problem you are trying to solve?

e.g. Why do you think you need this?

Let me try again. I am going to explain what I need to do and why, because without the context of the building material, the length of time things take only assumptions can be made and those will be wrong.

I need to control a heating system - a European evohome system. The system comprises 16 zones. Due to the cost of energy the system needs to be more adaptable and flexible than is possible via the standard app Honeywell provide. For this we already use another Alexa based system “SmartHeat” which has multiple schedules which we can load according to schedules and is excellent. However this does not have any automation which Home Assistant does. In the case of the Honeywell app, the control is fine for a couple of zones but not for 16. Family members struggle with the interface. In the case of Alexa, the voice recognition isn’t perfect and the Echo Show interface is limited.

So something better “everything at a glance” and simpler and more accurate is the goal. Customising Home Assistant seems the best option to provide the simple control and small level of automation we need to manage our energy consumption more effectively than is currently possible.

The primary issue with the HA inbuilt climate controls is there is no time bounding capability. This is a fundamental feature of evohome. All our zone overrides need to be “time bounded” so the system will resume to the existing program after the override period has ended.

However, in certain cases that may be inappropriate so exposing previous setpoint temperature [since that what the zone reverts to] is important. The rooms are entirely independent and are built of stone and brick so there is no transfer of heat from one room to another. They also take time to warm and cool, so the logic and the way things are done is not “normal” or fast. We have to think ahead and plan the house state well in advance. We also need the capacity to adapt to changes quickly, so the house has time to react.

Perhaps an example would illustrate. If we want to watch TV, currently I need to schedule the system to start warming our TV room at 4pm so by 7pm it is comfortable. When the time bounded override completes (say 9pm), the room will settle back to 8° overnight and so the programmed temperature is 6°. Otherwise we will be burning energy in a room warm we may not use again for several days. On the other hand, there are rooms where we are present or sleep for instance, that are heated round the clock for 8 months of the year.

The associated issues with programming errors if introduced into the system are significant. The standard HA climate controls expose, modes of presence for the system, off, heat etc. If anyone were to accidentally trigger “off” for instance, we would be in for burst pipes.

So now I have explained why and what. . .

{{ states('climate.back_door') }}

Returns a single string containing all the data I need in various attributes as do the other 16 zones (even though I can only get 12 of them with this automation but leave that for now).

Only one of these are exposed in any of the builtin UI elements, current temperature.
This is why I need access to these bits of data below.

{{ state_attr('climate.back_door', 'hvac_mode') }}
{{ state_attr('climate.back_door', 'min_temp') | float }}°C
{{ state_attr('climate.back_door', 'max_temp') | float }}°C
{{ state_attr('climate.back_door', 'current_temperature') | float }}°C
{{ state_attr('climate.back_door', 'preset_mode') }}
{{ as_timestamp(state_attr('climate.back_door', 'status').setpoints.this_sp_from) | timestamp_custom('%I:%M:%S %p') }}
{{ state_attr('climate.back_door', 'status').setpoints.this_sp_temp | float }}°c
{{ state_attr('climate.back_door', 'status').setpoints.next_sp_temp | float }}°c
{{ as_timestamp(state_attr('climate.back_door', 'status').setpoints.next_sp_from) | timestamp_custom('%I:%M:%S %p') }}
{{ state_attr('climate.back_door', 'status').setpoint_status.setpoint_mode }}
{{ as_timestamp(state_attr('climate.back_door', 'status').setpoint_status.until) | timestamp_custom('%I:%M:%S %p') }}
{{ state_attr('climate.back_door', 'status').setpoint_status.target_heat_temperature | float }}°c

This is where I feed back into the original post above.
What is the right way to design such an interface exposing and controlling all 16 of these zones and is there any reference material I can read.

In the automation you use to pre-heat the room capture the current state of the climate device with a “scene on the fly” Scenes - Home Assistant.
Change the temperature.
Then when finished watching tv apply the previously saved scene an then delete it.

Or if these temperatures are fixed and you know what they are you don’t even need to save the scene.

Maybe this scheduler card, together with the scheduler integration, is something that could mke thigs a bit easier?

Thank you - I will check both of these suggestions out properly tomorrow.

Both ideas are really helpful thank you, I am going to play around with the scheduler-card now. It occurs that an override is almost always going to be human initiated as opposed to scheduled. One of our group works one day a week in the office, but that can be any of three days and we can’t automate that since there is no electrical state anywhere to indicate it. Two of our group we can sort out with presence sensors since their spaces move only few degrees when they are in / out and react fast enough for that. Otherwise its all up to us [me] to drive it.

I started to build a custom bubble card based UI which exposes the four important variables along with a tap popup which then provided the setting mechanism custom card for any override needed. I had already written much of the code for that idea, but I though it would be sensible to write one override pop up which had the relevant climate entity passed into it when tapped. And this is where I started with my original post. How to pass in a climate entity, so I don’t need to code a repeat of the same code for 16 different climate zones. Then I started this post on how to pass variables and parameters, worried about doing things in the wrong way because I can see how this will grow and more kit and ideas will arrive in due course. That is driven because my profession is rectification of previous configuration and engineering errors.

If you look at my stats I’ve read around 500 odd posts in a couple of days and many of those are being generated because people have elements which don’t quite work right for various reasons. Sometimes it’s coding, sometimes it’s the target hardware and sometimes it’s the network and sometimes the approach is wrong. I don’t want to invest my limited time only to add to that list because my approach is screwed up and that is what drove the post.

I guess ultimately I am asking is it ok to write everything in yaml in the UI code by dynamically querying entity and setting states there or is it better to store entity state as other custom helper entities or as automations which I can call from the UI? Perhaps no one really knows, perhaps the code is so lightweight it doesn’t matter. Perhaps since all the states are in memory anyway, how the code queries and triggers these things is immaterial.

But it would be nice to be “told”.

I know evohome is old and there are now viable new kids on the block which would not need this investment. However here is my counter to those. I have used this system since 2002 and it has never ever failed, even for a single set point 1 time in 23 years. Honeywell are still around, still support it, still use the same Rameses protocol which doesn’t fail and doesn’t interfere with other stuff, copes with our walls etc. I have the same 20yr old controller running off a gateway and a newer controller type which has newer firmware and inbuilt wifi. The things just work. Heck I still have 4 of the original HR80 controllers part of the first starter kit and they are going fine at 23 years old! Let’s see if any of the new kids are around in 25 years on the original hardware . . .

The answer I was looking for is really in this video by Geoff. The best way to build what I want is to do is to use a template: !include filename.yaml statement in the configuration.yaml pointing to a file containing all the template yaml needed to expose the individual attributes of the sensor entities I am interested in.

This makes my task much quicker and easier; directly editing that file in VS Code attached via samba than using the web based helpers UI, which is great for things where 200~ sensors are not needed.

The architecture is:

  1. Expose all the sensor entities needed as unique ID’s in the templates file.
  2. Reference the unique ID’s in the rest of the code I define in the HA instance.

This provides the abstraction and archtecture methodology I was looking for.

Four major advantages:

  1. yaml is not going way - but the UI is an evolving entity from core dev.
  2. Proper repo for the code with all the benefits of versioning backup etc which means insurance.
  3. If any of the hardware is replaced at a future stage the code needs to be changed in one place. The template’s file.
  4. It abstracts the effort of building the UI from the vulnerability of platform change.

Thanks for the pointers to the cards and the suggestion again, appricated.