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.