How to trigger automation BEFORE set time?

I’ve read that Jinja was created for print formatting, so given it’s usage in HA
I think it’s used to pass python up to the sandbox for evaluation
Hence ‘jinja wrapped python’
This is a fairly well educated guess - but it’s still a guess :rofl:

Yeah, it’s meant to serve up information to static forms, like a webpage. Pretty much why it’s used in HA.

So here is my version

automation:
  - alias: 'Turn Car Heat On'
    initial_state: on
    trigger:
      platform: time_pattern
      minutes: '/1'
    condition:
      - condition:  template
        value_template: "{{ states('sensor.sonoffth3_am2301_temperature') | float < 2 }}"
    action:
      - service_template: >
          {% set timenow = as_timestamp(now()) %}
          {% set hr = states('input_number.alarm_clock3_hour') | int %}
          {% set mn = states('input_number.alarm_clock3_minute') | int %}
          {% set settime = as_timestamp(now().strftime("%Y-%m-%d " ~ '{0:0=2d}:{1:0=2d}'.format(hr, mn) ~ ':00')) %}
          {% set starttime = settime - (3*60*60) %}
          {% set slot = (starttime <= timenow < settime) %}
          {% set tempdeg = states('sensor.sonoffth3_am2301_temperature') | float %}
          {% set mins = 160 - (tempdeg * -16) %}
          {% set heatrqtm = starttime + mins * 60 %}
          {% set heat = slot and timenow >= heatrqtm %}
          {% if heat %}
            switch.turn_on
          {% else %}
            switch.turn_off
          {% endif %}
        entity_id: switch.sonoffpow3_2

Given me another 5 Mins and I’ll add some comments to explain it.
You will NEED to test this as it looks okay to me but I only have the template editor to test it as I have no actual entities suitable.

automation:
  - alias: 'Turn Car Heat On'
    initial_state: on
    trigger:
      platform: time_pattern
      minutes: '/1'
    condition:
      # this condition just stops the automation if your temp is 2° or higher, adjust as you see fit (cuts down on further processing)
      # Note: I also considered parsing the start and stop times here but it was nearley as much processing required as is in the service template
      # Note2 : the '>' is not required for a single line template but then it does require quotes
      # Note3 : Note that the quotes are double on the outside and single within
      - condition:  template
        value_template: "{{ states('sensor.sonoffth3_am2301_temperature') | float < 2 }}"
    action:
      - service_template: >
          {# This is a jinja comment and will be ignored by the interpreter #}
          {# ALL the below is indented more than it needs to be to show the comments more clearly #}
          {# This next line (all comments will be as this) gets the time RIGHT NOW and stores it as a HUGE number that has both date and time #}
            {% set timenow = as_timestamp(now()) %}
          {# this gets your input number hour (input_datetime is much easier) and stores as a number in hr #}
            {% set hr = states('input_number.alarm_clock3_hour') | int %}
          {# this gets your input number minute and stores as a number in mn #}
            {% set mn = states('input_number.alarm_clock3_minute') | int %}
          {# this sets your 'settime' as a huge number and stores in settime #}
            {% set settime = as_timestamp(now().strftime("%Y-%m-%d " ~ '{0:0=2d}:{1:0=2d}'.format(hr, mn) ~ ':00')) %}
          {# as we've already done most of the work we just subtract 3 hours from the set time to start our evaluation #}
            {% set starttime = settime - (3*60*60) %}
          {# this tests to see if we are 'in' the 'slot' #}
            {% set slot = (starttime <= timenow < settime) %}
          {# this gets the temperature (as a float, as that will aid in resolution) #}
            {% set tempdeg = states('sensor.sonoffth3_am2301_temperature') | float %}
          {# this gets the minute offset from the start based on the temperature #}
            {% set mins = 160 - (tempdeg * -16) %}
          {# given the 'offset' we now have a new 'start time' calculated, based on temperature #}
            {% set heatrqtm = starttime + mins * 60 %}
          {# this tests that we are (now) in the 'slot' and 'that switch on time' is in the past #}
            {% set heat = slot and timenow >= heatrqtm %}
          {# this uses the last evaluation to determine if we switch 'on' or 'off' #}
            {% if heat %}
              switch.turn_on
            {% else %}
              switch.turn_off
            {% endif %}
        entity_id: switch.sonoffpow3_2

Note: the above code is bulky and could easily be slimmed down, but this way it’s easier to maintain and when you come back to it in 5 years you’ll spend less time scratching your head saying “What the hell does that do ?”

Note: slimming it down is not the same as optimising it. Petro is a specialist at optimising stuff so he may come back with some suggested changes.

Now, Lets see what @Burningstone has to say ? (ie is it concise enough ?) {Edit: it’s 27 lines, the same as burnings but mine switches off if the temperature rises and at the end of the slot. I could also make it shorter but readability would suffer :rofl: )

Edit: note it will switch on (assuming it’s cold enough) every minute, this is not a problem for switches and lights etc because it doesn’t turn them off first so the switch will not even notice.
You could ‘condition test’ to see if the switch was ‘on’ and required ‘on’ OR ‘off’ and required ‘off’ but that would have to go in the condition and again that’s a LOT of additional and unecessary processing.

Edit2: the ‘formula’ is based on a 3 hour assessment period and an assumption that the max we need is 3 hours and the minimum is 20 mins at 2 degrees (1.25° means NO heating, 160 - (1.25 * -16) = 180 so it wont even be considered)

2 Likes

Didn’t read all of it yet, but your value template is missing one curly brace at the beginning and at the end :shushing_face:

1 Like

Eh ! Where ? But I wouldn’t be surprised :man_shrugging: :rofl:

I did find a mistake, its a pair of curlies on the condition but is there another ???

This:

Should be this:
"{{ states('sensor.sonoffth3_am2301_temperature') | float < 5 }}"

:upside_down_face:

snap !

10 char min

Gilean,
Also note that this automation does not use your sensor, the values are taken direct from your input numbers.
As I said it took a while to get these conditioned as meaningful timestamps (petro said the same)
Input datetime’s are much easier

Wow, great work! As I have two cars, I thought I could try this one instead for one of them, and leave the previous code (for now) for the other half’s car as it seems to work! :smiley:

But if I understood right, this is solution to problem where temperature goes up and down within three hours and prevents the situation when heater should be turned on but it doesn’t.

But how does this work?

… and there you explained the part that was not clear to me :slight_smile: I was thinking that where are those fixed temperatures that determine how much early you need to start heating, but seems like it’s pretty dynamic! Although I am not sure about those calculations here… Say it’s 2 degrees. 160 - (2 * -16) = 160 - ( -32) = 192 minutes? And say it’s -2 degrees. 160 - (-2 * -16) = 160 - 32 = 126. And your example, 1,25 degrees, would be 160 - 20 = 140? Did you mean + instead of - ?

Good point, didn’t notice that. But to me it’s just the same, as that sensor.alarm_clock3_time is there only to make the frontend show the full time instead of separate hour and minute :slight_smile:

So 192 minutes after the start evaluation is 12mins after it should finish, so it won’t turn on (we already established that 1.25 (or more) degrees means no action given this formula)

Well 128 actually, but that’s 128 minutes after start time so 52 minutes of heating

1.25 degrees is 160 - (1.25 * -16) == 160 - (-20) ==160 + 20 == 180
So no, I meant what I said.
You may need to plot this in excel or something
X = temperature from say - 12 to + 5
And this needs to give you minutes before set time
I assumed 180 at - 10 going to 20 minutes at - 2 (and then drawing a straight line through those points)
But then these numbers need to be converted to mins added to start time, this works out at the formula I gave, but this is just a working example, tweak it to suit you.

1 Like

This is amazing, great job! I love how you made this simple automation smarter and fuel saving at the same time with only a few lines of code, and also commented very well. Highly appreciate posts like this, you went the extra mile, massively optimized the automation and all this “just” to help someone here on the forum. Kudos to you!

1 Like

You flatter me.
The ‘persons’ helped here were Gilean, ‘you’ and ‘ME’ with an emphasis on the ME.
I enjoy solving puzzles and this was a good one

Though @Gilean now has me wondering about my formula as I just did that in my head and now I’m starting to doubt it as not all the bricks are in neat rows !
Damn, I’m gonna have to draw some graphs :tongue: so you may have a point Gilean

Edit: AND if anyone can improve on this I would welcome such as I’ll learn even more (everyday should be a school day ! )

Ah yes, now I get it! Actually I don’t need to tweak that formula at all since it seems to work the way I want, only better! I like the fact that heating time increases in linear way instead of hard-coded steps at certain temperature points! I think it works and it’s almost bedtime here so I set my alarm_clock3 at 07:00, the heater is not plugged (as I am working from home tomorrow), but I can see from recorder when exactly my sonoff has triggered and what kind of outdoor temperature there has been at that moment. Exciting :slight_smile:

Your formula is totally fine. I quickly put this into Excel, hope you can read it. Green = heat, red = don’t heat.

Hahaha ! Guess what I’ve been doing for the last 15 minutes ?
Doing the exact same thing though I plotted a graph of temp (on a 0.1 resolution) against minutes.
Yeah, my formula worked, but I really shouldn’t do stuff like that in my head, hence why I had doubts

So I assume you’d have tackled this in appdaemon. And as such would probably have a lot of similarities ??? (never played with it).
It would be interesting to hear your comparative views.

My main aim with any of these is to achieve the desired result using as little power as possible (I hate waste, and power = CO2 )

@Mutt, I marked your code as solution because it did work! :slight_smile: So thank you a lot! Well, not initially as I had commented out platform time_date as I thought it would not be needed here, but after taking that part back in, it started to work! :slight_smile: So, I set up my alarm_clock to 9:50 (when I was supposed to leave), and now when I look at my logs, the heater switch has turned on at 9:06, and the outdoor temperature was -1,5C. And the switch has turned off at 9:50 as expected. So, my car would have been pre-heated for 44 minutes. Not sure if that proves the math right or wrong, but at least the heating time seems more or less right :slight_smile:
And, with this code I don’t need extra automation to switch off the heater as this one takes care of that as well :slight_smile:

1 Like

Eh ! Where did I use that ?
The only platform I thought I’d used was the time pattern trigger (needed for it to fire)
All my code was explained and necessary.

The solution ? Not sure this should take the solution away from @Burningstone as he addressed the problem as stated in the title, having said that I’m conflicted as the better solution (again until someone points out a mistake or improvement) is the variable timing one, And others visiting the thread should use that but I’m not invested too much so what do you and burning think ?

-1.5 * -16 is 24, 160 - 24 = 136, 180 - 136 = 44 so the arithmetic agrees
If you need a quadratic equation instead of a linear one we could do that too but I’d have no idea how you’d get the data

Gilean, maybe you should change the title of the thread to something like “Variable Car Preheating for Morning Use” as that is more what you wanted and covers more of the bases. ???

I don’t care about getting the “solution”, my solution counter is high enough :stuck_out_tongue: :rofl:

I think people should use your solution (couldn’t find any mistake) in case they face the same kind of problem. However I agree with you, that my solution is the solution that solves the question in the title of this topic, so for future readers it might be better to mark my post as the solution, because this can be applied to other problems that require to trigger before a set time, which don’t have anything to do with heating.

Up to you @Gilean, to decide.

Yes I would have done this in AppDaemon and it would probably be very similar. I mean, it’s just Python.

The difference would be, that I would create a more “general” app, which could be used for other automations that require the same kind of logic and a config file may then be as simple as something like this:

heat_car_him:
  module: awesome_python_app
  class: AwesomeClass
  set_time_entity: sensor.alarm_clock_him
  offset: "03:00:00"
  heating_per_hour_celsius: 0.5
  max_temp_celsius: 2.0
  switch: switch.car_heater_him

And then for an additional car

heat_car_her:
  module: awesome_python_app
  class: AwesomeClass
  set_time_entity: sensor.alarm_clock_her
  offset: "03:00:00"
  heating_per_hour_celsius: 0.5
  max_temp_celsius: 2.0
  switch: switch.car_heater_her

This would also be more dynamic, meaning I can “generate” the required math formula from the config. Let’s say he gets a new heater for his car that is more powerful and therfore requires less time to heat the same as the old heater. Then he would need to adjust all the math formulas in each automation with your approach. I know this could be done as well in a normal HA automation.

Turns out that you probably didn’t :slight_smile: I noticed that this morning NONE of my automations that use time did not trigger, and that’s because they use platform time_date. Then I thought your automation used that as well because that automation did not trigger either, but I think I had wrong switch in my automation (cannot remember anymore as I have tweaked these again), but I can confirm that works now! :slight_smile:

I think this linear thing works, the only tweak I think I should do is that heating time is a bit longer near zero temperature, as even -1C is still such temperature that windshield is frozen and ~30 minutes might not be enough for clearing it. But I have to see how it acts.

Hmm, not sure about that, as I wouldn’t myself tried to find solutions with those words, as I thought this is more of general problem, about automation triggering before alarm time or something like that. What do others think?

Yep, this is a good example of a case where it should be possible to mark TWO posts as solution because they both are! :smiley: But I figured that I mark Burningstone’s solution back as solution, because although Mutt’s solution is more… sophisticated, so to speak :slight_smile: , Burningstone’s solution answers my original question better. Although I still ended up using Mutt’s code :smiley:

1 Like