Heaty will die, Schedy be born!

https://hass-apps.readthedocs.io/en/stable/apps/schedy/schedules/expressions/index.html

EDIT: And, more specifically: https://hass-apps.readthedocs.io/en/stable/apps/schedy/schedules/expressions/helpers/state.html#state

thx, but I don’t understand how to add that to my code:

schedy_heating:  # This is our app instance name.
  module: hass_apps_loader
  class: SchedyApp
  watched_entities:
  - group.job_laura
  - input_number.default_temp
  actor_type: thermostat
  schedule_append:
  - v: "OFF"

  rooms:
    living:
      rescheduling_delay: 1
      actors:
        climate.heater:
      schedule:
      - v: 19.5
        rules:
        - weekdays: 1-5
          rules:
          - rules:
            - x: "Next() if state('group.job_laura') == 'home' else Break()"
            - { start: "06:30", end: "21:30" }
        - weekdays: 6-7
          rules:
          - rules:
            - x: "Next() if state('group.job_laura') == 'home' else Break()"
            - { start: "08:00", end: "22:00" }            
      - v: 16

so I’d like to replace v: 19.5 with input_number.default_temp

Ah… as you’ve probably read, v is for fixed values only. Since you want to refer to the state of an entity (like you also did in your conditional sub-schedule with group.job_laura), you have to use x to create an expression. Now just return the state of the entity that stores the temperature from your expression instead of setting a fixed value and you’re done:

x: state(...)
1 Like

awesome, that did it! I tried x: before but didn’t know the exact formatting.
x: state(‘input_number.default_temp’) did the trick.

Yes, exactly. Seems there’s no straight example for such a usecase in the docs, I’ll add one soon.

1 Like

I’m trying to use Schedy with my blinds. I’ve found your code as the only example. May i ask: have you converted it to generic2 actor type? I’ve tried, but no success. Could you eventually help? Thanks!

@tristone, I was inspired by your configuration for schedy + frontend time settings, I wonder how do you handle heating times that cross midnight, with the in range function you use, the built in schedy start and end futureshas the great +1d option that goes automaticallywhen start time is > end time, but how can we achieve the same when using expressions?

I dont want to hard code the +1d as the time is dynamic and might not always be so. Sometimes it might not cross midnight…
(time.hour*60+time.minute+1) in range(int(state(“sensor.bedroom_work_first_on”)), int(state(“sensor.bedroom_work_first_off”)))

Hi @yabuts. In fact it is very simple to answer - I do not expect going over midnight :slight_smile: Any of my scenarios need such behavior.

But I suppose it might not be so problematic, maybe. I am using in the expression the range evaluation. But if you put someting like ( > start and < end) even if it looks weird it might work. Probably it would require one more varibale specifying if it is going over midnight or not and have a rule for each case separately.

How will > start and < end work? If end is after midnight, resulting end to actually be < start.

Hi. Would be better if confirmed by @roschi but I think it should work.
It is eXpression, not Value so not handled by the internal Schedy timers but just evaluated for true / false nad returnign the value. So at midnight nothign will happen adn the value will be not changed and kept. I hope :slight_smile:

I made for you an suggestion based on my setup. Update it accordingly for you and try and let me know :wink:

First, new binary sensor to evaluate if we have set times over midnight or not:

platform: template
sensors:
  bedroom_overnight:
    entity_id:
      - sensor.bedroom_work_first_on
      - sensor.bedroom_work_first_off
    value_template: >-
      {{ int(state("sensor.bedroom_work_first_on")) > int(state("sensor.bedroom_work_first_off")) }}

And then modify Schedy.
Instead of this:

- x: state("input_number.bedroom_high_temperature") if (state("input_boolean.bedroom_work_first_active") == "on") and (state("binary_sensor.workday_status") ==
 "on") and (time.hour*60+time.minute+1) in range(int(state("sensor.bedroom_work_first_on")), int(state("sensor.bedroom_work_first_off"))) else Next()

put two expressions, one almost as current one and the otrher for over midrnigh scenario:

 - x: state("input_number.bedroom_high_temperature") if (state("input_boolean.bedroom_overnight") == "off") and (state("input_boolean.bedroom_work_first_active") == "on") and (state("binary_sensor.workday_status") ==
 "on") and (time.hour*60+time.minute+1) in range(int(state("sensor.bedroom_work_first_on")), int(state("sensor.bedroom_work_first_off"))) else Next()
- x: state("input_number.bedroom_high_temperature") if (state("input_boolean.bedroom_overnight") == "on") and (state("input_boolean.bedroom_work_first_active") == "on") and (state("binary_sensor.workday_status") ==
 "on") and ( ((time.hour*60+time.minute+1) > int(state("sensor.bedroom_work_first_on"))) or ((time.hour*60+time.minute+1) < int(state("sensor.bedroom_work_first_off"))) ) else Next()

It is e X pression, not V alue so not handled by the internal Schedy timers but just evaluated for true / false nad returnign the value. So at midnight nothign will happen adn the value will be not changed and kept. I hope :slight_smile:

Well, don’t make this assumption please. Schedules could be re-evaluated at any time. Schedy actually does this quite frequently, for instance at every single start or end time you have in your schedule, or when a schedy_reevaluate event comes in. So don’t rely a schedule is only re-evaluated at specific times, you have no control over it.

Why not a simple function like this:

expression_environment: |
  def time_between(start, end):
      current = time.hour * 60 + time.minute + 1
      if start >= end:
          # spans midnight
          return current >= start or current < end
      return current >= start and current < end
- x: "state('sensor.temp') if time_between(int(state('sensor.start')), int(state('sensor.end'))) else Next()"

And then fire schedy_reevaluate every N minutes, as fine as you want the resolution to be.

Just a (untested) draft though.

BTW, where do these integer sensors come from? Given that you make times configurable via the frontend, why not use input_datetime with only has_time: true and then query the timestamp attribute? Should be way easier than to use sensors in between.

1 Like

Hi @roschi
Yes, I realized lter that the assumption is not correct. I wanted to say something a bit different in fact :slight_smile:

As @yabuts is inspiring himself in my setup I was just continuing. And there are no times defined directly in config file of Schedy. So no rule to set something at midnight itself.

The integer sensors are coming from my UI where is independent slider for hours and minutes. So computing the overal value of minutes since midnight. The datetime was not usable (or maybe even present) 3 years ago. Maybe it is time to check and refactor :slight_smile:

Ah, ok, just wanted to make sure no one assumes schedule re-evaluation happens predictably.

I haven’t used input_datetime myself yet, just saw it in the docs and thought it would fit for this usecase quite nicely.

@roschi your time_between function looks neat and simple, going to try it. But why not came up with something to allow the built in schedy start and end take a variable or state, this will open schedy to vary new possibilities and make my schedules alot cleaner not having to write expression (same for start_date end_date)

Thanks

That would not fit into the way schedules are built internally. Not saying that it couldn’t be done with a lot of refactoring, but at the end it would make the whole evaluation much more complicated and Schedy’s codebase larger.

In general, I’m not for extending the core schedule semantics more and more. Instead, there is the expression system that lets you use Python for whatever you want. I could though think about providing something like time_between() as an expression helper natively, maybe even something a little smarter. But currently I’m totally busy with other things.

Hi @roschi,

schedy is still doing great here! :slight_smile: Thanks again for the great work.

One more question: Is it possible to add a window sensor to more than one room? I’d like to add all room sensors to the hallway as well, so the heating in the hallway turns off whenever the window of a room is open. This scenario is not mentioned in the docs.

Hi @breti

One more question: Is it possible to add a window sensor to more than one room? I’d like to add all room sensors to the hallway as well, so the heating in the hallway turns off whenever the window of a room is open. This scenario is not mentioned in the docs.

Yes, totally possible.

You could do it like so (untested):

- x: |
    for s in state("binary_sensor"):
        if s["state"] == "on" and s["attributes"].get("window_room"):
            result = Mark(OFF, Mark.Overlay
            break
    else:
        result = Next()

If you only want to include the sensors of specific rooms, compare the room name against a set of names: s["attributes"].get("window_room") in ("room1", "room2")

Don’t forget to add all desired window sensors to watched_entities of the hallway as well.

Best regards
Robert

Hi @roschi,

thanks for the quick response!

What is the appropriate syntax for the window_room attribute for multiple rooms?

Hi there, i just moved from RPi to Nuc and thought that’s a good reason to start with a all fresh setup. Were there any breaking changes regarding the window detection that I didn’t notice? Everything but this seems to work:

'binary_sensor.0x00158d0001de8815_contact' changed from 'off' to 'on', reevaluating <Room R:living>.
2020-05-02 18:24:01.110719 INFO schedy_heating: --- [R:living] Doing schedule re-evaluation in 1 second [reset=False]
2020-05-02 18:24:02.009340 INFO schedy_heating: --- [R:living] Evaluating room's schedule (reset=False, force_resend=False).
2020-05-02 18:24:02.011291 INFO schedy_heating: --- [R:living] Assuming it to be 2020-05-02 18:24:02.009903.
2020-05-02 18:24:02.012638 INFO schedy_heating: --- [R:living] ������ [SUB]  <<Schedule 'living'>/1:<Rule with sub <Schedule 'prepend'>>>
2020-05-02 18:24:02.013817 INFO schedy_heating: --- [R:living]     ������ [ACT]  <<Schedule 'living'>/1/1:<Rule x='Mark(OFF, Mark.OVERLAY) if not is_empty('...>>
2020-05-02 18:24:02.015039 INFO schedy_heating: --- [R:living] Initializing expression helper: BasicHelper, order = 0
2020-05-02 18:24:02.016337 INFO schedy_heating: --- [R:living] Initializing expression helper: PatternHelper, order = 0
2020-05-02 18:24:02.017454 INFO schedy_heating: --- [R:living] Initializing expression helper: ScheduleHelper, order = 0
2020-05-02 18:24:02.018844 INFO schedy_heating: --- [R:living] Initializing expression helper: StateHelper, order = 0
2020-05-02 18:24:02.022251 INFO schedy_heating: --- [R:living] Initializing expression helper: ThermostatExpressionHelper, order = 0
2020-05-02 18:24:02.024416 INFO schedy_heating: --- [R:living] Initializing expression helper: CustomEnvironmentHelper, order = 1000
2020-05-02 18:24:02.033328 INFO schedy_heating: --- [R:living]     ������ => Next()

The sensor is listed as a watched_entity in the room “living”, the customize.yaml has the following entry:

binary_sensor.0x00158d0001de8815_contact:
  window_room: living

this is the prepend following the documentation and carried over from my previous setup:

  schedule_prepend:
    - x: "Mark(OFF, Mark.OVERLAY) if not is_empty(filter_entities('binary_sensor', state='on', window_room=room_name)) else Next()"

EDIT: Found the mistake. I forgot that the customize.yaml has to be manually included into the configuration.yaml

Hi.

Thanks for the time_between function.

I was adding today air conditioning to the HA and Schedy and immediatelly used it. And as it is working fine I refactored also heating :slight_smile:

And the input_datetime, well, still not usable. Technically OK, but not at all user-friendly in Lovelace.