Occupancy - An unusual automation question

Okay, I have occupancy sorted, it works well and I’m on top of the usual occupancy automations (heating, lights etc.)
But Music ?
I have a number of amplifiers built into boxes that connect to talk to my music server over ethernet (wired or wireless) and each box has a z-wave switch built-in, the pi inside the box runs picore so I can drop power and be 99.99999% sure it’ll be okay.
Eack Box has controls to bring it on/turn it off twice a day, or once all day, each slot can be disabled and separately adjusted.
So since having a discussion with @petro I’m reassessing my approach.
I want to turn the music off when I leave the house (a doddle, just turn it off) BUT what I want to happen is that it turns back on when I come back.
So two problems (even given the petro approach, and its the best one I’ve seen) is to create binary sensors for each ‘on slot’ of each device (I may go out during the first on slot and come back during the second on slot).
I’m looking for suggestions here only : -
So my current thoughts - I have two binary sensors that now come on in the time slots instead of the amps, if I’m in the amps come on (if in an enabled slot) and go off when I leave, so a simple automation triggered on both slot on, or occupied on, conditional on both these same conditions.
I could also have a ‘bit’ per box that allows override, so a Box could be company for the dog if we’re out.
Okay now the tricky bit.
Normally an on slot starts before it finishes (obvious, and easy to test for in a binary sensor) but what happens when (say) the slot starts at 22:00 and stops at 01:00 ? Again easy to check for if it’s always this way but who can know what a particular user will set for any given slot.
Do I need (say) appdaemon to test if start is less than stop, making the normal case, but inverting it if stop is earlier than start ???
If so how would you pass out the precedure to the binary sensor - or is there another way? Can jinja come to the rescue ?
Any views or suggestions welcome.
Mutt

Okay, I’ve mulled this over for quite some time and decided to get started.
I have put together the folowing binary_sensor but the config check does not like the 2 lines before the resultant value line. (I’ve commented out various lines to narrow it)
Probably because I’m putting an IF ELSE on one line requiring a SET
Dunno, I’m guessing !
I’ve read a LOT of links on template sensors and I vaguely recall something similiar (that’s why I did it this way) but though I can find examples of almost everything within the template, I cant find anything on the IF ELSE with a SET action

binary_sensor:
  - platform: template
    sensors:
      bs_pifi01_on:
        entity_id: sensor.time
        value_template: >
          {% set time = states('sensor.time') %}
          {% set occd =  is_state('input_boolean.bs_occupied', 'on') %}
          {% set tmron = is_state('input_boolean.ib_switch_pifi01_on', 'on') %}
          {% set slt1en = is_state('input_boolean.ib_switch_pifi01_timeslot1_enable', 'on') %}
          {% set slt1start = states('input_datetime.id_switch_pifi01_tmeslt1_on') [0:5] %}
          {% set slt1sttop = states('input_datetime.id_switch_pifi01_tmeslt1_off') [0:5] %}
          {% set slt2en = is_state('input_boolean.ib_switch_pifi01_timeslot2_enable', 'on') %}
          {% set slt2start = states('input_datetime.id_switch_pifi01_tmeslt2_on') [0:5] %}
          {% set slt2sttop = states('input_datetime.id_switch_pifi01_tmeslt2_off') [0:5] %}
          {% if (slt1start < slt1stop) set slt1on = (slt1start <= time < slt1stop) else set slt1on = (slt1start <= time or time < slt1stop) endif %}
          {% if (slt2start < slt2stop) set slt2on = (slt2start <= time < slt2stop) else set slt2on = (slt2start <= time or time < slt2stop) endif %}
          {{ occd and (tmron or slt1on or slt2on) }}
        friendly_name: Pi-Fi-01 Requested On

The Config Check Throws this back : -

Invalid config for [binary_sensor.template]: invalid template (TemplateSyntaxError: expected token ‘end of statement block’, got ‘then’) for dictionary value @ data[‘sensors’][‘bs_pifi01_on’][‘value_template’]. Got "{% set time = states(‘sensor.time’) %} {% set occd = is_state(‘input_boolean.bs_occupied’, ‘on’) %} {% set tmron = is_state(‘input_boolean.ib_switch_pifi01_on’, ‘on’) %} {% set slt1en = is_state(‘input_boolean.ib_switch_pifi01_timeslot1_enable’, ‘on’) %} {% set slt1start = states(‘input_datetime.id_switch_pifi01_tmeslt1_on’) [0:5] %} {% set slt1sttop = states(‘input_datetime.id_switch_pifi01_tmeslt1_off’) [0:5] %} {% set slt2en = is_state(‘input_boolean.ib_switch_pifi01_timeslot2_enable’, '… (See ?, line ?). Please check the docs at https://home-assistant.io/components/binary_sensor.template/

The tests are to cope with a time slot passing through midnight (clock as opposed to sun)
So 23:00 to 01:00 as an example.
I know that I’m comparing text strings but as they are in the same format and increment the same way, it should not be a problem.
Have removed the THEN’s

I’m sorry but I don’t understand what you want to achieve. Maybe I’m just too stupid.
Can you please explain again what your end goal is? :sweat_smile:

About the template sensor, I think this should be a working example with in ‘set’ in an IF THEN ELSE

{% if (a < b) %}
  {% set x = y %}
{% else %} 
  {% set z = s %}
{% endif %}

I’m no expert in this Jinja and templating stuff, but from basic programming, this doesn’t make any sense to me:

{{ occd and (tmron or slt1on or slt2on) }}

What should be the value?

Let’s say:

  • occd = true
  • tmron = false
  • slt1on = true
  • slt2on = false

so this translates to:

{{ true and (false or true or false }}

How can you evaluate this (false or true or false)? Is it true or false?

occd is house occupied
tmron is timer running ((say) ‘switch on’, ‘runs for 60 minute timer’, ‘switches off’)
slt1on is time slot 1 on (runs between 2 datetime values)
slt2on is time slot 2 on (runs between 2 other datetime values)

Tranlates to {{ true and (false or true or false) }}, correct ! (though you missed a bracket - putting this into the template editor acheives ‘true’)
So it will NOT be true if occd is FALSE but also reguires ONE of (tmron, slt1on, slt2on) to be true
hence the 'or’s between (and the brackets).

This is to get something that is ‘on’ when I leave the house, to switch off when the house is unoccupied, but then switch back on when I return IF a valid time slot has the item active.

I’m sorry, I must have mixed something up. You are totally right.

I still don’t understand what you want to achieve :slightly_smiling_face:

So let’s say you are at home listening to some music in the living room, then you leave the house and the music turns off. When you return home you want to turn the music in the living room on again but only if the time slot for the living room is enabled. Is this correct? If yes, why do you want to do this? When I’m at home and listen to music somewhere, then leave the house to have dinner with a friend. Why should I want to continue music in the living room or anywhere at all when I return home?

Exact usage.
Because I don’t want music 24 hrs a day and only want it on when I’m at home and not when I’m working and not when I’m watching television or in bed.

Breaking it out on to 5 lines of IF ELSE ENDIF passes the checker, I’m rebooting to see if it actually works.
But I’d still like to get this onto a single line if I can
I could do this with some faily extended boolean logic (long lines) but was hoping to economise

that’s not how you do inline if statements.

          {% set slt1on  = (slt1start <= time < slt1stop) if (slt1start < slt1stop) else (slt1start <= time or time < slt1stop) %}
(if-passed-result) if (if-statement) else (if-failed-result)

@petro
So, “set slt1on =” is ‘implied’ in the second set ?
Sorry just seen your edit, but same question ?

i.e. should not (if-failed-result) be :- “set slt1on = (slt1start <= time or time < slt1stop)”

Inline if statements return 1 result. The result that is returned depends on if the if statement passes or fails.

Okay so the "set variable_name = " is implied for the else case
Got it
Cheers

May I ask why you want to have it in one line?
In multiple lines the readability is way better for my taste.

Okay, with the binary sensor re-written as : -

binary_sensor:
  - platform: template
    sensors:
      bs_pifi01_on:
        entity_id: sensor.time
        friendly_name: Pi-Fi-01 Requested On
        value_template: >
          {% set time = states('sensor.time') %}
          {% set occd =  is_state('input_boolean.bs_occupied', 'on') %}
          {% set tmron = is_state('input_boolean.ib_switch_pifi01_on', 'on') and is_state('input_boolean.ib_switch_pifi01_timer_enable', 'on') %}
          {% set slt1en = is_state('input_boolean.ib_switch_pifi01_timeslot1_enable', 'on') %}
          {% set slt1start = states('input_datetime.id_switch_pifi01_tmeslt1_on') [0:5] %}
          {% set slt1sttop = states('input_datetime.id_switch_pifi01_tmeslt1_off') [0:5] %}
          {% set slt2en = is_state('input_boolean.ib_switch_pifi01_timeslot2_enable', 'on') %}
          {% set slt2start = states('input_datetime.id_switch_pifi01_tmeslt2_on') [0:5] %}
          {% set slt2sttop = states('input_datetime.id_switch_pifi01_tmeslt2_off') [0:5] %}
          {% set slt1on = (slt1start <= time < slt1stop) if (slt1start < slt1stop) else (slt1start <= time or time < slt1stop) %}
          {% set slt2on = (slt2start <= time < slt2stop) if (slt2start < slt2stop) else (slt2start <= time or time < slt2stop) %}
          {{ occd and (tmron or (slt1on and slt1en) or (slt2on and slt2en)) }}

I get the following error from the checker : -

Invalid config for [binary_sensor.template]: invalid template (TemplateSyntaxError: expected token ‘,’, got ‘slt2on’) for dictionary value @ data[‘sensors’][‘bs_pifi01_on’][‘value_template’]. Got "{% set time = states(‘sensor.time’) %} {% set occd = is_state(‘input_boolean.bs_occupied’, ‘on’) %} {% set tmron = is_state(‘input_boolean.ib_switch_pifi01_on’, ‘on’) and is_state(‘input_boolean.ib_switch_pifi01_timer_enable’, ‘on’) %} {% set slt1en = is_state(‘input_boolean.ib_switch_pifi01_timeslot1_enable’, ‘on’) %} {% set slt1start = states(‘input_datetime.id_switch_pifi01_tmeslt1_on’) [0:5] %} {% set slt1sttop = states(‘input_datetime.id_switch_pifi01_tmeslt1_off’) [0:5] %} {% set slt2… (See ?, line ?). Please check the docs at https://home-assistant.io/components/binary_sensor.template/

So presumably it’s missing a comma somewhere “,” ???

If I can do it here, I can do it elsewhere.
i.e. complete one line templates
Once debugged and I undestand ‘why’ then readability won’t be a problem
(I can read both of the one line offereing given so far - they just don’t work :rofl: )

you got errors. specifically slt1sttop is declared wrong, i.e. you use it slt1stop.

Always best to use the template editor to check your results.

I also listed a binary_sensor as input_boolean … doh !
Current Template : -

          {% set time = states('sensor.time') %}
          {% set occd =  is_state('binary_sensor.bs_occupied', 'on') %}
          {% set tmron = is_state('input_boolean.ib_switch_pifi01_on', 'on') and is_state('input_boolean.ib_switch_pifi01_timer_enable', 'on') %}
          {% set slt1en = is_state('input_boolean.ib_switch_pifi01_timeslot1_enable', 'on') %}
          {% set slt1start = states('input_datetime.id_switch_pifi01_tmeslt1_on') [0:5] %}
          {% set slt1stop = states('input_datetime.id_switch_pifi01_tmeslt1_off') [0:5] %}
          {% set slt2en = is_state('input_boolean.ib_switch_pifi01_timeslot2_enable', 'on') %}
          {% set slt2start = states('input_datetime.id_switch_pifi01_tmeslt2_on') [0:5] %}
          {% set slt2stop = states('input_datetime.id_switch_pifi01_tmeslt2_off') [0:5] %}
          {% set slt1on = (slt1start <= time < slt1stop) if (slt1start < slt1stop) else (slt1start <= time or time < slt1stop) %}
          {% set slt2on = (slt2start <= time < slt2stop) if (slt2start < slt2stop) else (slt2start <= time or time < slt2stop) %}
          {{ occd and (tmron or (slt1on and slt1en) or (slt2on and slt2en)) }}

Edit: Just seen your latest (below), I’ll debug this for a while and when working I’ll try the new offering
Cheers

personally, i’d go a macro approach and name you devices appropriately. Especially if you have to replicate this in multiple places. Then only only have to edit 2 fields on each copy.

{%- macro isSlotEnabled(current_time, common_string) %}
{%- set enabled = is_state('input_boolean.%s_enabled'%common_string, 'on') %}
{%- set start = states('input_datetime.%s_on'%common_string)[0:5] %}
{%- set end = states('input_datetime.%s_off'%common_string)[0:5] %}
{%- set slot_on = (start <= time < end) if (start < end) else (start <= time or time < end) %}
{{ enabled and slot_on }}
{%- endmacro %}

{% set time = states('sensor.time') %}
{% set slot1 = isSlotEnabled(time, 'ib_switch_pifi01_timeslot1') %}
{% set slot2 = isSlotEnabled(time, 'ib_switch_pifi01_timeslot2') %}
{% set occd =  is_state('input_boolean.bs_occupied', 'on') %}
{{ occd and (slot1 or slot2) }}

Okay I tested every component and got the template to : -

binary_sensor:
  - platform: template
    sensors:
      bs_pifi01_on:
        entity_id: sensor.time
        friendly_name: Pi-Fi-01 Requested On
        value_template: >
          {% set time = states('sensor.time') %}
          {% set occd =  is_state('binary_sensor.bs_occupied', 'on') %}
          {% set tmron = is_state('input_boolean.ib_switch_pifi01_on', 'on') and is_state('input_boolean.ib_switch_pifi01_timer_enable', 'on') %}
          {% set slt1en = is_state('input_boolean.ib_switch_pifi01_timeslot1_enable', 'on') %}
          {% set slt1start = states('input_datetime.id_switch_pifi01_tmeslt1_on') [0:5] %}
          {% set slt1stop = states('input_datetime.id_switch_pifi01_tmeslt1_off') [0:5] %}
          {% set slt2en = is_state('input_boolean.ib_switch_pifi01_timeslot2_enable', 'on') %}
          {% set slt2start = states('input_datetime.id_switch_pifi01_tmeslt2_on') [0:5] %}
          {% set slt2stop = states('input_datetime.id_switch_pifi01_tmeslt2_off') [0:5] %}
          {% set slt1on = (slt1start <= time < slt1stop) if (slt1start < slt1stop) else (slt1start <= time or time < slt1stop) %}
          {% set slt2on = (slt2start <= time < slt2stop) if (slt2start < slt2stop) else (slt2start <= time or time < slt2stop) %}
          {{ occd and (tmron or (slt1on and slt1en) or (slt2on and slt2en)) }}

This passes all checks in the template evaluation for all inputs I can muster and returns the correct result.
However, in the config check it still flags : -
Invalid config for [binary_sensor.template]: invalid template (TemplateSyntaxError: expected token ‘,’, got ‘slt2on’) for dictionary value @ data[‘sensors’][‘bs_pifi01_on’][‘value_template’]. Got "{% set time = states(‘sensor.time’) %} {% set occd = is_state(‘binary_sensor.bs_occupied’, ‘on’) %} {% set tmron = is_state(‘input_boolean.ib_switch_pifi01_on’, ‘on’) and is_state(‘input_boolean.ib_switch_pifi01_timer_enable’, ‘on’) %} {% set slt1en = is_state(‘input_boolean.ib_switch_pifi01_timeslot1_enable’, ‘on’) %} {% set slt1start = states(‘input_datetime.id_switch_pifi01_tmeslt1_on’) [0:5] %} {% set slt1stop = states(‘input_datetime.id_switch_pifi01_tmeslt1_off’) [0:5] %} {% set slt2e… (See ?, line ?). Please check the docs at https://home-assistant.io/components/binary_sensor.template/

So I’m drawing a line under this one (unless anyone can spot what is wrong) and am persuing @Burningstone 's suggestion on the multi-line evaluation, I’ll report back with my findings
I’ll then move on to Petro’s ‘macro’ approach.

I think you should break this up. It would be less management but more sensors. Templates would be easier to understand.

binary_sensor:
  - platform: template
    sensors:
      bs_pifi01_slot1:
        entity_id: sensor.time
        friendly_name: Pi-Fi-01 Slot 1
        value_template: >
          {% set t = states('sensor.time') %}
          {% set enabled = is_state('input_boolean.ib_switch_pifi01_timeslot1_enable', 'on') %}
          {% set start = states('input_datetime.id_switch_pifi01_tmeslt1_on') [0:5] %}
          {% set stop = states('input_datetime.id_switch_pifi01_tmeslt1_off') [0:5] %}
          {{ start <= t < stop if start < stop else (start <= t or t < stop) }}
      bs_pifi01_slot2:
        entity_id: sensor.time
        friendly_name: Pi-Fi-01 Slot 2
        value_template: >
          {% set t = states('sensor.time') %}
          {% set enabled = is_state('input_boolean.ib_switch_pifi01_timeslot2_enable', 'on') %}
          {% set start = states('input_datetime.id_switch_pifi01_tmeslt2_on') [0:5] %}
          {% set stop = states('input_datetime.id_switch_pifi01_tmeslt2_off') [0:5] %}
          {{ start <= t < stop if start < stop else (start <= t or t < stop) }}
      bs_pifi01_on:
        friendly_name: Pi-Fi-01 Requested On
        value_template: >
          {% set timer = is_state('input_boolean.ib_switch_pifi01_on', 'on') and is_state('input_boolean.ib_switch_pifi01_timer_enable', 'on') %}
          {% set occupied =  is_state('binary_sensor.bs_occupied', 'on') %}
          {% set slot1 = states('binary_sensor.bs_pifi01_slot1') %}
          {% set slot2 = states('binary_sensor.bs_pifi01_slot2') %}
          {{ occupied and (timer or slot1 or slot2) }}

Thanks Petro
I managed to get the single line and Burningstone’s multi-line IF ELSE passing the template editor evaluation. - BUT neither pass config check.
Both times it appears to be : - “expected token ‘,’, got ‘slt2on’) for dictionary value” that it fails on but I just can’t see it.
I’m next going to try a long boolean approach, followed by your macro and then your multi-sensor concept. I’ll do them all just to be thorough.
This is frustrating, but I’m learning and the experience will be valuable.
Thanks for your efforts so far.

That’s why you should break it up. It will point to the issue sooner. Also, your variable names and entity_id’s are hard as hell to read. It will only make this harder. There’s always a good phrase to keep in mind, I tell myself this all the time. “Keep it simple, stupid”. It’s called the KISS principle.