Is there a way to return a value from a script or create a script subroutine usable by multiple scripts or extent the template functions?

I’m currently working on building a bunch of automations to control all my thermostats. I’ve got 12 thermostats with a total of 20 setpoints (8 have heat-to and cool-to setpoints, and 4 are only heat-to). I’m trying to build a 4-stage / weekday-vs-weekend + vacation-mode configuration. I have a bunch of input_number settings to store all the setpoints (one set per stage per day) and I’ve already written a script it iterate through all the thermostats to save (initialize) the input_number settings. This is done as a python-script.

The other automation I have is that I want to “remember” the settings if they are changed locally. In other words, if I modify a thermostat setting at the thermostat, I want to record and remember that setting based upon the current time-stage setting. I have this implemented, but it currently saves to only one of the TOD settings; it is not dynamic.

As I work through this, I’m finding that I’d like to build a subroutine that determines the specific time-stage/day setting for a particular thermostat given the current time/day. I don’t think I can easily do this via a template. Basically, I need to do something like:

{% set thermo = entity_id | replace('climate.','',1) -%}
{% set weekday_weekend = ... %}
{% set tod = (based on current time, determine the morning/day/evening/night timeframe %}

Part of the reason I want this in a subroutine is that I need the same code in many different scripts/automations. I would need it in the ‘save’ automation, but I also need it in the “update thermostats” automations. Moreover, due to the fact that some thermostats have two settings and some only have one (temperature, target_temp_high, and target_temp_low) I need to either build conditional actions or separate automations. I haven’t figured out how to make conditional actions, or ways to set two values in one call, so right now I have one script to run for my 8 high/low thermostats and another one for the 4 temp thermostats.

So back to my question: IF I could do this as a subroutine, then I could really simplify the rest of my automation and scripting. I could literally do

{% set day_time_info = SUBROUTINE(entity_id) %}

Unfortunately I have NO CLUE how to actually do what I want. I have not found any way to “return” a value, or extend the template capabilities to add a new function.

Is this doable?

1 Like

@warlord, ever find a solution? I’m looking for similar techniques. Would love to hear about what you came up with.

@tgpaul – sorry, never found a solution, so I wrote a python_script to do it, and still have to copy-and-paste some common code between scripts (but it’s less code to copy). And the HA Core Devs wouldn’t accept my 4-line change to enable Jinja include-files/macros.

1 Like

Bummer. Did they offer any explanation or rationale? I read in a bunch of threads that this is all by design, since the Home Assistant model is intended to be immutable, though I’m not sure exactly how supporting free functions violates immutability.

Have you looked into this? https://github.com/custom-components/pyscript

1 Like

You’re supposed to use helpers to pass information around. Create the helper, get the information in the template using states(), set the information with a service call.

1 Like

Very interested.

I combed documentation and the forum all weekend and didn’t find anything like this. Are you referring to this? http://dev-docs.home-assistant.io/en/master/api/helpers.html

I assumed most of the Home Assistant API was locked down from python_script's context based on this description:

Name Description
hass The Home Assistant object. Access is only allowed to call services, set/remove states and fire events. API reference

Wasn’t particularly surprised, since you probably want to avoid long-lived scripts and thus would need to lock out most async stuff (i.e. event listeners).

Is there a good example I could follow to implement helpers? Really excited that this could be a game-changer for me.

No, helpers: input_number, input_boolean, input_datetime, input_select, input_text.

Sorry, I’m not understanding. How can this be used to create subroutines?

sorry, if you want to create subroutines, use a script that you call via another script and pass variables. If the variables are accessed from an outside source (I.e. a global variable) then use helpers mentioned above. You can’t dynamically create a script on the fly.

example of sub script:

script:
  my_subroutine:
    sequence:
      service: xyz.abc
      data:
        somefield: "{{ my_variable }}"
  routine1:
    sequence:
      service: script.my_subroutine
      data:
        my_variable: 7
  routine2:
    sequence:
      service: script.my_subroutine
      data:
        my_variable: 37

So if I understand correctly, if I want to get data out of the subroutine, it itself would need to update a global variable (helper) which the calling routine would need to know about and fetch after completion. Does that mean all service calls in a sequence are blocking?

Is there no other way to get data directly from subroutine (return or output variable)?

1 Like

How about we start with what you’re actually trying to do instead of this specific niche case?

1 Like

the way i addresed this need for subroutines is to create a template entity in the config that takes input values from helpers such as a text or number input.
the template entity updates its state and any attributes you build based on the template code and takes input.
for use, i set the input fields to a value and then get the response (so to speak) from the template entity

2 Likes

In pyscript, you can create persistent variables.
You can use these variables in a template.
I have a pyscript that counts lights that are on,off, unavailable. The result is saved in persistent fields. I use these fields at several places in the ui.