Light on when trigger "true", off when trigger "false"

Hi!

I’ve been struggling for some time with turning a light on and off, with the help of an automation. After a couple of days of reading the trigger has been solved, but I’m stuck at the action part. In short the automation is made for a light/switch that turns on a car heater a specific time before departure - after departure time I want the light/switch to be turned off.

The trigger part returns “True” as it should when the light/switch is to be turned on, and “False” when the light/switch is to be turned off. What I want is for the switch to actually be turned off when the trigger reports “False”.

Some people suggest adding an extra automation to turn the light off, but imagine there has to be a better way.

Anyone have any ideas of what I could do solve this in one automation?

alias: Motorvärmare (test)
description: ''
trigger:
  - platform: template
    value_template: >-
      {% set dtime = now().strftime("%Y-%m-%d ") %}

      {% set time = now().timestamp() %}

      {% set dur = states('input_number.motorvarmare_minuter') | int*60 %}

      {% set motorvarmare_on = strptime(dtime +
      states('input_datetime.motorvarmare_start'), '%Y-%m-%d
      %H:%M:%S').timestamp() - dur %}

      {% set motorvarmare_off = strptime(dtime +
      states('input_datetime.motorvarmare_start'), '%Y-%m-%d
      %H:%M:%S').timestamp() %}

      {{ motorvarmare_on <= time <= motorvarmare_off }}
condition: []
action:
  - service: light.turn_on
    data: {}
    entity_id: light.brytare10210
mode: single

When this template:

{{ motorvarmare_on <= time <= motorvarmare_off }}

evaluates to True it triggers the automation and the action is executed.

When it evaluates to False the automation is not triggered and the action is not executed.

1 Like

Indeed, if you want to trigger on true and false, put the template in a template binary sensor and use the state of that sensor as the trigger.

1 Like

I am trying to understand this template:

      {% set motorvarmare_on = strptime(dtime +
      states('input_datetime.motorvarmare_start'), '%Y-%m-%d
      %H:%M:%S').timestamp() - dur %}

Can you give me an example of a typical state value for input_datetime.motorvarmare_start? Is it something like 2020-12-13 10:25:12? Or is it just a time like 10:25:12?

Also, do the on and off times remain within the same day or can they span more than a single day?

That seems like a reasonable option, instead of relying on the automation trigger itself. I’m new to Home Assistant so I don’t know all the ins and outs.

As soon as I’ve put the kids to bed I’ll see if i can make it work.

Thanks!

1 Like

As far as I understand, the function is meant to add date (from now() ) to the variable from input_datetime, which is displayed as 10:25:12, which in turn is a timestamp from 00:00:00 current day whereas now() as a timestamp calculates from 1900-01-01.

This allows me to calculate switch on time, dependant on input_datetime, from the same baseline as now() uses.

Edit: more can be read here, Calculating and formatting a time calculation

Thank you all for pointing me in the right direction. I got it to work!

It was less of a hassle than I initially thought but I would have preferred to keep it in one automation rather than using a binary_sensor too. I’m sure there’s a reasonable explanation for not having that possibility.

All that needs to be done now are some finishing touches to entity names and such.

This is my binary_sensor:

  - platform: template
    sensors:
      motorvarmare_binary_sensor:
        friendly_name: "motorvarmare_binary_sensor"
        value_template: >-
          {% set dtime = now().strftime("%Y-%m-%d ") %}
          {% set time = now().timestamp() %}
          {% set dur = states('input_number.motorvarmare_minuter') | int*60 %}
          {% set motorvarmare_on = strptime(dtime + states('input_datetime.motorvarmare_start'), '%Y-%m-%d %H:%M:%S').timestamp() - dur %}
          {% set motorvarmare_off = strptime(dtime + states('input_datetime.motorvarmare_start'), '%Y-%m-%d %H:%M:%S').timestamp() %}
          {{ motorvarmare_on <= time <= motorvarmare_off }}

And this is my automation:

alias: Motorvärmare (test)
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.motorvarmare_binary_sensor
condition: []
action:
  - choose:
      - conditions:
          - condition: state
            entity_id: binary_sensor.motorvarmare_binary_sensor
            state: 'on'
        sequence:
          - service: light.turn_on
            data: {}
            entity_id: light.brytare10210
    default:
      - service: light.turn_off
        data: {}
        entity_id: light.brytare10210
mode: single
1 Like

Glad you got sorted :slightly_smiling_face:

I would just point out though that your automation is overly complicated, this will do the exact same thing…

alias: Motorvärmare (test)
trigger:
  platform: state
  entity_id: binary_sensor.motorvarmare_binary_sensor
action:
  service: light.toggle
  entity_id: light.brytare10210
1 Like

What I was leading up to is that your template is employing far more time conversions, notably via strptime, than are needed for this application. In fact, there’s no need to use it at all.

{% set t = (now().time() | string)[:5] %}
{% set s = state_attr('input_datetime.motorvarmare_start', 'timestamp') %}
{% set d = states('input_number.motorvarmare_minuter') | int * 60 %}
{% set offset = (s - d) | timestamp_custom('%H:%M', false) %}
{{ offset <= t <= states('input_datetime.motorvarmare_start') }}
  1. First line gets the current time (as a string).
  2. Second line gets the timestamp (as an integer) directly from the input_datetime.
  3. Third line calculates the duration (as an integer).
  4. Fourth line computes the offset by subtracting the duration from the timestamp then converts it to a time string.
  5. Last line performs a string comparison to determine if the current time string lies between the other two time strings.
1 Like

Oh, that’s a lot neater.

I started with using sensor.time, instead of now(), partly because it seemed more similiar to input_datetime and partly because I couldn’t figure out how to reduce now() to a timestamp I could work with. So I took a bit of inspiration here and a bit of inspiration there.

What does the [:5] do specifically, in the first row?

It’s python slice notation. It means to slice the first 5 characters of the string.

Paste that line into the Template Editor and experiment with it.

Thanks. This makes my life a lot easier. :slight_smile:

I have no use in calculating my automations from year 1900.

This will only work if there are no other interactions with light.brytare10210. Otherwise it will simply toggle whichever state the light is in at the time (rather than specifically shutting it off or on.)

If there’s a need to maintain the binary_sensor and light states in sync:

alias: Motorvärmare (test)
trigger:
  platform: state
  entity_id: binary_sensor.motorvarmare_binary_sensor
action:
  service: "light.turn_{{ trigger.to_state.state }}"
  entity_id: light.brytare10210

Indeed, I misread the ‘choose’ and thought it was saying if the light was on turn it off, rather than the binary sensor. Otherwise I would have suggested exactly that.

@qoheleth I’m not sure I follow. Do you mean that the automation won’t work/trigger if the light state has been manually altered?
Edit: Never mind, I see now that you were replying to another answer.

@123 Does this mean that it wont be possible to manually change the light state, when binary_sensor and light are in sync?

In this particular case the light is a 433Mhz RFLink entity, so I’m not receiving any acknowledgement anyway. But for the sake of learning…

That “light.turn_{{ trigger.to_state.state }}” construction was exactly what I was looking for for another application. (It saves splitting the action to turn_on and turn_off).
Thanks for that.