{% if 'Cool' not in states.input_select.lounge_ac_mode.state %}
or
{% if (states.input_select.lounge_ac_mode.state not in ['Normal Cool','Silent Cool','Powerful Cool'] %}
{% if 'Cool' not in states.input_select.lounge_ac_mode.state %}
or
{% if (states.input_select.lounge_ac_mode.state not in ['Normal Cool','Silent Cool','Powerful Cool'] %}
As my input_datetime values have no date I went with doing the check in seconds as per the suggestion from @petro.
- condition: or # And only if current time is within *active* AM or PM automation time ranges
conditions:
- condition: template
value_template: >
{% set no_date_timestamp = now().second + (now().minute * 60) + (now().hour * 3600) %}
'{{ (states.input_datetime.lounge_ac_am_on_time.attributes.timestamp <= no_date_timestamp < states.input_datetime.lounge_ac_am_off_time.attributes.timestamp) and states.input_boolean.lounge_ac_am_automation.state == 'on'}}'
- condition: template
value_template: >
{% set no_date_timestamp = now().second + (now().minute * 60) + (now().hour * 3600) %}
'{{ (states.input_datetime.lounge_ac_pm_on_time.attributes.timestamp <= no_date_timestamp < states.input_datetime.lounge_ac_pm_off_time.attributes.timestamp) and states.input_boolean.lounge_ac_pm_automation.state == 'on'}}'
And itās still not working even though it all evaluates fine in the template editor.
I ended up making binary template sensors and using those in the conditions:
- platform: template
sensors:
lounge_ac_am_automation_time_active:
friendly_name: "Lounge AC AM Automation Time Active"
value_template: >-
{{ (now().second + (now().minute * 60) + (now().hour * 3600)) >= states.input_datetime.lounge_ac_am_on_time.attributes.timestamp
and (now().second + (now().minute * 60) + (now().hour * 3600)) <= states.input_datetime.lounge_ac_am_off_time.attributes.timestamp
and is_state('input_boolean.lounge_ac_am_automation', 'on')}}
lounge_ac_pm_automation_time_active:
friendly_name: "Lounge AC PM Automation Time Active"
value_template: >-
{{ (now().second + (now().minute * 60) + (now().hour * 3600)) >= states.input_datetime.lounge_ac_pm_on_time.attributes.timestamp
and (now().second + (now().minute * 60) + (now().hour * 3600)) <= states.input_datetime.lounge_ac_pm_off_time.attributes.timestamp
and is_state('input_boolean.lounge_ac_pm_automation', 'on')}}
This seems to work. I suspect the reason why it wasnāt working in the condition_template has something to do with the quotes I had around the template. Iām going to keep it this way rather than fiddling further as the binary template sensors simplify the code as I use them as conditions and triggers in more than one place.
EDIT 3: nope. god damn now() doesnāt update templates. Not only that but changing the other sensors in the template are hit and miss too.
Iāve spent over a day working on this and am no closer to finding a solution. It souldnāt be this hard to tell if a time falls within a range.
EDIT 4: After a day and a half I gave up. Iāve worked around it with four automations and two input_booleans.
- id: lounge_ac_set_am_automation_time_active # stupid workaround for lack of time only timestamp
alias: 'Lounge AC Set AM Automation Time Active'
hide_entity: true
trigger:
platform: template
value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_am_on_time.state[0:5]) }}"
condition:
condition: state
entity_id: input_boolean.lounge_ac_am_automation # Only if automation is required
state: 'on'
action:
service: input_boolean.turn_on
data:
entity_id: input_boolean.lounge_ac_am_automation_time_active
- id: lounge_ac_reset_am_automation_time_active
alias: 'Lounge AC Reset AM Automation Time Active'
hide_entity: true
trigger:
platform: template
value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_am_off_time.state[0:5]) }}"
condition:
condition: state
entity_id: input_boolean.lounge_ac_am_automation # Only if automation is required
state: 'on'
action:
service: input_boolean.turn_off
data:
entity_id: input_boolean.lounge_ac_am_automation_time_active
- id: lounge_ac_set_pm_automation_time_active
alias: 'Lounge AC Set PM Automation Time Active'
hide_entity: true
trigger:
platform: template
value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_pm_on_time.state[0:5]) }}"
condition:
condition: state
entity_id: input_boolean.lounge_ac_pm_automation # Only if automation is required
state: 'on'
action:
service: input_boolean.turn_on
data:
entity_id: input_boolean.lounge_ac_pm_automation_time_active
- id: lounge_ac_reset_pm_automation_time_active
alias: 'Lounge AC Reset PM Automation Time Active'
hide_entity: true
trigger:
platform: template
value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_pm_off_time.state[0:5]) }}"
condition:
condition: state
entity_id: input_boolean.lounge_ac_pm_automation # Only if automation is required
state: 'on'
action:
service: input_boolean.turn_off
data:
entity_id: input_boolean.lounge_ac_pm_automation_time_active
Sorry, been out of town. Iāll take a look in the morning.
I havenāt read the whole topic, but to this one point, if you want to use now() in a template binary_sensor/sensor, you can just explicitly list all the entities the template references (use the entity_id config parameter), and add sensor.time to the list (assuming youāve enabled that sensor.) That will force the template to update once a minute. Again, I didnāt read the whole topic, so there may be a more efficient way to do what you want. @petro has been helping you with this, so in general youāre in good hands.
Thanks @petro. No hurry, I have a working (if somewhat inelegant) workaround with the automations and input_booleans.
Thatās definitely worth investigating. Thanks.
So is there any reason you didnāt try this template?
condition:
- condition: template
value_template: >
{% set current_date_string = now().strftime("%Y-%m-%d ") %}
{% set current_time = now().timestamp() %}
{% set lounge_am_start = strptime(date+state('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set lounge_am_end = strptime(date+state('input_datetime.lounge_ac_am_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set lounge_pm_start = strptime(date+state('input_datetime.lounge_ac_pm_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set lounge_pm_end = strptime(date+state('input_datetime.lounge_ac_pm_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{{ lounge_am_start <= current_time <= lounge_am_end or lounge_pm_start <= current_time <= lounge_pm_end }}
Also, pretty much the reason most of your posts arenāt working is because you are treating everything as a string instead of a number.
These attributes:
states.input_datetime.lounge_ac_am_on_time.attributes.timestamp
always return strings. Pretty much everything coming back from home assistant is a string. In some cases, it will properly convert it for you but that is very unlikely.
Second, I recommend avoiding grabbing from the objects directly unless you have to. Instead of using:
states.input_datetime.lounge_ac_am_on_time.attributes.timestamp
use:
state_attr('input_datetime.lounge_ac_am_on_time','timestamp')
Also, I believe the timestamps on your input_datetimes actually indlude todayās date. Otherwise the timestamps would be pointless. Which brings us back to using the following condition:
condition:
- condition: template
value_template: >
{% set current_time = now().timestamp() %}
{% set am_start = states('input_datetime.lounge_ac_am_on_time', 'timestamp') | int %}
{% set am_end = states('input_datetime.lounge_ac_am_on_time', 'timestamp') | int %}
{% set pm_start = states('input_datetime.lounge_ac_am_on_time', 'timestamp') | int %}
{% set pm_end = states('input_datetime.lounge_ac_am_on_time', 'timestamp') | int %}
{{ am_start <= current_time <= am_end or pm_start <= current_time <= pm_end }}
But, it looks like youāre having trouble finding a trigger? So I need to ask, what is your overall goal with this automation? Are you trying to turn on an AC mode at a certain time each day?
Iām thinking you need 2 template sensors, one for am and one for pm. Then your automation will trigger off that turning on and off. If thatās the case, then you just need to set that up:
sensor:
# This platform turns on the datetime sensor. It has the name sensor.date__time because
# whoever set this up decided to convert 'date & time' to the name which produces
# 2 underscores... annoying.
- platform: time_date
display_options:
- 'date_time'
binary_sensor:
- platform: template
sensors:
lounge_ac_am:
value_template: >
{% set current = strptime(states('sensor.date__time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set start = states('input_datetime.lounge_ac_am_on_time', 'timestamp') | int %}
{% set end = states('input_datetime.lounge_ac_am_off_time', 'timestamp') | int %}
{{ is_state('input_boolean.lounge_ac_am_automation','on') and start <= current <= end }}
lounge_ac_pm:
value_template: >
{% set current = strptime(states('sensor.date__time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set start = states('input_datetime.lounge_ac_pm_on_time', 'timestamp') | int %}
{% set end = states('input_datetime.lounge_ac_pm_off_time', 'timestamp') | int %}
{{ is_state('input_boolean.lounge_ac_pm_automation','on') and start <= current <= end }}
Now all you need to do is build 4 automations:
trigger:
- platform: state
entity_id: binary_sensor.lounge_ac_am
to: 'on'
from: 'off'
trigger:
- platform: state
entity_id: binary_sensor.lounge_ac_am
to: 'off'
from: 'on'
trigger:
- platform: state
entity_id: binary_sensor.lounge_ac_pm
to: 'on'
from: 'off'
trigger:
- platform: state
entity_id: binary_sensor.lounge_ac_pm
to: 'off'
from: 'on'
Iām sure that can be shortened depending on your end goal as well.
I did actually try casting the timestamps as integers. It made no difference.
No they really only have a time. I have defined them this way so that they can be applied to any day, e.g.
lounge_ac_pm_on_time:
name: PM on time
has_date: false
has_time: true
e.g. {{ state_attr(āinput_datetime.lounge_ac_am_on_timeā,ātimestampā) }} returns 21600 when set to 06:00 (6x60x60 seconds).
During a time range (e.g. AM_on time to AM_off time) if the temperature of the room exceeds a trip temperature, cool the room, or if the temperature falls below another tirip piont heat the room. I originally had it set up to just check the temperature at the on time and turn the AC off at the off time. But often the temperature was falling (or rising) at the trigger time and the heating or cooling needed to come on a short time later. I have a bit of logic to stop oscillation between heat/cool or on/off too. Hereās whatās working (using the automations and input booleans I talked about in my last post:
- id: lounge_aircon_auto_on
alias: 'Lounge Room Aircon Scheduled On'
hide_entity: false
trigger:
- platform: state # AM on time trigger
entity_id: input_boolean.lounge_ac_am_automation_time_active
to: 'on'
- platform: state # PM on time trigger
entity_id: input_boolean.lounge_ac_pm_automation_time_active
to: 'on'
- platform: state # also trigger if heating is required during on time (condition below)
entity_id: binary_sensor.lounge_ac_heat_required
to: 'on'
- platform: state # also trigger if cooling is required during on time (condition below)
entity_id: binary_sensor.lounge_ac_cool_required
to: 'on'
condition:
condition: and
conditions:
- condition: state # Only if AC is off (prevents oscillation between heat and cool mode if temperature over/undershoots set point)
entity_id: input_select.lounge_ac_mode
state: 'Off'
- condition: or # And only if room heating or cooling is required (for the case of a time trigger)
conditions:
- condition: state
entity_id: binary_sensor.lounge_ac_heat_required
state: 'on'
- condition: state
entity_id: binary_sensor.lounge_ac_cool_required
state: 'on'
- condition: or # And only if current time is within *active* AM or PM automation time ranges (for the case of a temperature trigger)
conditions:
- condition: state
entity_id: input_boolean.lounge_ac_am_automation_time_active
state: 'on'
- condition: state
entity_id: input_boolean.lounge_ac_pm_automation_time_active
state: 'on'
- condition: or # And only if workday not tested or if is a workday
conditions:
- condition: state
entity_id: input_boolean.lounge_ac_workday
state: 'off'
- condition: state
entity_id: binary_sensor.workday_sensor
state: 'on'
action:
- service: input_select.select_option
data_template:
entity_id: input_select.lounge_ac_mode
option: >
{% if states.sensor.aeotec_lounge_temperature.state < states.input_number.lounge_ac_heat_temp_set.state %}
Normal Heat
{% elif states.sensor.aeotec_lounge_temperature.state > states.input_number.lounge_ac_cool_temp_set.state %}
Normal Cool
{% endif %}
Iāll have a look at the rest of your post and suggestions later. Too worn out to think tonight.
Thank you for taking the time to help with this.
Ok, so if this is true:
Then you have to use this method with all your datetimes:
{% set cdate = now().strftime("%Y-%m-%d ") %}
{% set ctime = now().timestamp() %}
{% set am_start = strptime(cdate + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set am_end = strptime(cdate + states('input_datetime.lounge_ac_am_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set pm_start = strptime(cdate + states('input_datetime.lounge_ac_pm_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set pm_end = strptime(cdate + states('input_datetime.lounge_ac_pm_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
Which leads to this automation. I optimized and removed unnecessary conditions that you had and simplified your triggers. Also added safety in your action:
- id: lounge_aircon_auto_on
alias: 'Lounge Room Aircon Scheduled On'
hide_entity: false
trigger:
- platform: state # AM on time trigger
entity_id:
- binary_sensor.lounge_ac_heat_required
- binary_sensor.lounge_ac_cool_required
to: 'on'
condition:
- condition: state # Only if AC is off (prevents oscillation between heat and cool mode if temperature over/undershoots set point)
entity_id: input_select.lounge_ac_mode
state: 'off'
- condition: template
value_template: >
{% set d = now().strftime("%Y-%m-%d ") %}
{% set t = now().timestamp() %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set am_end = strptime(d + states('input_datetime.lounge_ac_am_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set pm_start = strptime(d + states('input_datetime.lounge_ac_pm_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set pm_end = strptime(d + states('input_datetime.lounge_ac_pm_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{{ am_start <= t <= am_end or pm_start <= t <= pm_end }}
- condition: or # And only if workday not tested or if is a workday
conditions:
- condition: state
entity_id: input_boolean.lounge_ac_workday
state: 'off'
- condition: state
entity_id: binary_sensor.workday_sensor
state: 'on'
action:
- condition: template
value_template: >
# verify that the temperature is actually different so that the option in the next
# action is always populated.
{{ states.sensor.aeotec_lounge_temperature.state != states.input_number.lounge_ac_heat_temp_set.state }}
- service: input_select.select_option
data_template:
entity_id: input_select.lounge_ac_mode
option: >
{% if states.sensor.aeotec_lounge_temperature.state < states.input_number.lounge_ac_heat_temp_set.state %}
Normal Heat
{% elif states.sensor.aeotec_lounge_temperature.state > states.input_number.lounge_ac_cool_temp_set.state %}
Normal Cool
{% endif %}
Sorry to keep butting in, but I just wanted to let you know this is not true. State strings are definitely always strings, no matter what they might look like. But attributes can be other types, such as int, float, boolean, datetime, dictionaries, lists, tuples, etc. E.g., the timestamp attribute of an input_datetime is definitely not a string:
And theyāre not getting converted by anything. They are just their raw Python type. When states are set into the state machine, the āstateā is definitely converted to a string, but the attributes are left whatever they were.
Ah. Now I remember why I didnāt use this:
In the template editor this:
{% state('input_datetime.lounge_ac_am_on_time') %}
Returns:
Error rendering template: TemplateSyntaxError: Encountered unknown tag 'state'.
I follow your logic and it is amazing except for this one problem.
Goodnight for real this time.
Because state is a typo, it should be states()
anyways, use this:
- id: lounge_aircon_auto_on
alias: 'Lounge Room Aircon Scheduled On'
hide_entity: false
trigger:
- platform: state # AM on time trigger
entity_id:
- binary_sensor.lounge_ac_heat_required
- binary_sensor.lounge_ac_cool_required
to: 'on'
condition:
- condition: state # Only if AC is off (prevents oscillation between heat and cool mode if temperature over/undershoots set point)
entity_id: input_select.lounge_ac_mode
state: 'off'
- condition: template
value_template: >
{% set d = now().strftime("%Y-%m-%d ") %}
{% set t = now().timestamp() %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set am_end = strptime(d + states('input_datetime.lounge_ac_am_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set pm_start = strptime(d + states('input_datetime.lounge_ac_pm_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
{% set pm_end = strptime(d + states('input_datetime.lounge_ac_pm_off_time'), '%Y-%m-%d %H:%M').timestamp() %}
{{ am_start <= t <= am_end or pm_start <= t <= pm_end }}
- condition: or # And only if workday not tested or if is a workday
conditions:
- condition: state
entity_id: input_boolean.lounge_ac_workday
state: 'off'
- condition: state
entity_id: binary_sensor.workday_sensor
state: 'on'
action:
- condition: template
value_template: >
# verify that the temperature is actually different so that the option in the next
# action is always populated.
{{ states.sensor.aeotec_lounge_temperature.state != states.input_number.lounge_ac_heat_temp_set.state }}
- service: input_select.select_option
data_template:
entity_id: input_select.lounge_ac_mode
option: >
{% if states.sensor.aeotec_lounge_temperature.state < states.input_number.lounge_ac_heat_temp_set.state %}
Normal Heat
{% elif states.sensor.aeotec_lounge_temperature.state > states.input_number.lounge_ac_cool_temp_set.state %}
Normal Cool
{% endif %}
Hi petro,
In the template editor this:
{% set d = now().strftime("%Y-%m-%d ") %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M').timestamp() %}
returns the following error:
Error rendering template: UndefinedError: āstr objectā has no attribute ātimestampā
What does this return?
{{ states('input_datetime.lounge_ac_am_on_time') }}
Seems like there is a typo somewhere because when I replace that state in my template, i get an answer. Are you sure you have the correct entity_id? Are you sure that state returns āHH:MMā time format?
{{ states('input_datetime.lounge_ac_am_on_time') }}
returns: 06:00:00
{{ strptime( states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M') }}
Also returns: 06:00:00
Keep in mind that the strptime function implementation in jinja returns the first string, instead of a datetime object, if the first string doesnāt match the second (format) string.
Ok so
{% set d = now().strftime("%Y-%m-%d ") %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M') %}
{{ am_start }}
Returns: 2018-09-18 06:00:00
But what does:
{% set d = now().strftime("%Y-%m-%d ") %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M') %}
{{ am_start }}
{{ am_start is string }}
return?
The issue seems to be your input_datetime is returning HH:MM:SS instead of HH:MM, which is weird, because I always thought they only returned HH:MM.
EDIT: Ok, my bad! input_datetimeās always return HH:MM:SS for the time part. I was thinking of sensor.time which only returns HH:MM.
So the problem is your format string. It needs to be '%Y-%m-%d %H:%M:%S'
.
2018-09-18 06:00:00
True
I definitely only supply HH:MM in the frontend, but itās states are:
<template state input_datetime.lounge_ac_am_on_time=06:00:00; has_date=False, has_time=True, hour=6, minute=0, second=0, timestamp=21600, friendly_name=AM on time @ 2018-09-17T23:05:10.040873+10:00>
So they add secondsā¦ this should be used then:
{% set d = now().strftime("%Y-%m-%d ") %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M:%S').timestamp() %}
which leads to this:
- id: lounge_aircon_auto_on
alias: 'Lounge Room Aircon Scheduled On'
hide_entity: false
trigger:
- platform: state # AM on time trigger
entity_id:
- binary_sensor.lounge_ac_heat_required
- binary_sensor.lounge_ac_cool_required
to: 'on'
condition:
- condition: state # Only if AC is off (prevents oscillation between heat and cool mode if temperature over/undershoots set point)
entity_id: input_select.lounge_ac_mode
state: 'off'
- condition: template
value_template: >
{% set d = now().strftime("%Y-%m-%d ") %}
{% set t = now().timestamp() %}
{% set am_start = strptime(d + states('input_datetime.lounge_ac_am_on_time'), '%Y-%m-%d %H:%M:%S').timestamp() %}
{% set am_end = strptime(d + states('input_datetime.lounge_ac_am_off_time'), '%Y-%m-%d %H:%M:%S').timestamp() %}
{% set pm_start = strptime(d + states('input_datetime.lounge_ac_pm_on_time'), '%Y-%m-%d %H:%M:%S').timestamp() %}
{% set pm_end = strptime(d + states('input_datetime.lounge_ac_pm_off_time'), '%Y-%m-%d %H:%M:%S').timestamp() %}
{{ am_start <= t <= am_end or pm_start <= t <= pm_end }}
- condition: or # And only if workday not tested or if is a workday
conditions:
- condition: state
entity_id: input_boolean.lounge_ac_workday
state: 'off'
- condition: state
entity_id: binary_sensor.workday_sensor
state: 'on'
action:
- condition: template
value_template: >
# verify that the temperature is actually different so that the option in the next
# action is always populated.
{{ states.sensor.aeotec_lounge_temperature.state != states.input_number.lounge_ac_heat_temp_set.state }}
- service: input_select.select_option
data_template:
entity_id: input_select.lounge_ac_mode
option: >
{% if states.sensor.aeotec_lounge_temperature.state < states.input_number.lounge_ac_heat_temp_set.state %}
Normal Heat
{% elif states.sensor.aeotec_lounge_temperature.state > states.input_number.lounge_ac_cool_temp_set.state %}
Normal Cool
{% endif %}