Time condition - inclusive or exclusive?

Correct, at is not valid for a time condition (docs). You could have a template condition comparing sensor.time with "16:00".

So you want to turn the light on if it is cloudy and either 16:00 or a range of times around sunset? I don’t believe you can use before_offset with after:sunset (docs).

Please paste the entire automation and a super-clear explanation of what you’re trying to achieve. I’m struggling to understand the span around sunset, or the relevance of 16:00.

The range of times is because I want it to trigger when it’s around sunset, during times that may be getting dark but not always - without interfering with other automation I have that control the same lights at/after sunset nor during the day. That part has been working fine.

I’m sure it could use some refining, but at this point I’m still not 100% certain exactly what I want to specify all the requirements. More or less, if it’s late day or nearing sunset, I want to turn on lights if it’s cloudy, or if it becomes cloudy during that time. Then I decided I also want it to turn on lights at a specific earlier time if it’s cloudy, even if sunset is much later.

I only got started with this back in Jan/Feb and my rules worked great…until I noticed with a summer storm even though sunset was much later I wanted it on at an earlier “evening” time like a traditional lamp timer I’m replacing (which would dumbly waste power turning on even if it’s sunny). Adding the “at 16:00” handles the case I hadn’t considered of “it’s stormy all day and sunset is late in summer”.

The biggest problem I’m having is figuring out how to translate what (in my head as a programmer) I am thinking about “less-than / less-than equal-to / just equal-to” type conditions I’d do in C++/Java to the words-ing that YAML uses to play with the options I think I want. In a “normal” program I’d just have an event loop trigger at 16:00 and add an “or” to my if-checks to “now == 16:00” but with YAML that’s an at in one place and apparently you can’t at it in another place as an “equal” condition so ??? and then I want to keep things reasonably maintainable so I am trying to avoid having several automation that do the same thing on minimally different conditions (in a program I’d use “functions” to encapsulate but that’s not a thing apparently with YAML).

  - alias: "Kitchen Lamp Timers On Cloudy Evening"
    trigger:
      # If it's near sunset, or stops being sunny, or hits 16:00, try and do it
      - platform: sun
        event: sunset
        offset: "-01:30:00"
      - platform: template
        value_template: "{{ states('weather.home') != sunny }}"
      - platform: time
        at: '16:00:00'
    condition:
      # If it's getting near (but still early) sunset, or at 16:00, let it go thru
      - condition: or
        conditions:
          - condition: sun
            after: sunset
            after_offset: "-01:30:00"
            before_offset: "-00:45:00"
          - condition: time
            after: '15:59:59'
            before: '16:00:01'
      # but only if it's not sunny (cloudy, rainy, foggy, whatever)
      - condition: template
        value_template: "{{ states('weather.home') != sunny }}"
    action:
      service: input_boolean.turn_on
      data:
        entity_id: input_boolean.kitchen_lamp_timers_on

I’m open to other ideas, but I have “daytime” automations I don’t want to overlap with and “approaching sunset” automations I don’t want to overlap with. I believe that has been working correctly though.

Some of this is also finding what DO I want it to do. I’m still not used to the flexibility of more than a dumb lamp timer…which I would just manually push in some extra pins if it was stormy for some days to make it on longer, or pull them out as the days were shorter. This can do it automatically but I haven’t thought of WHY I want it on, used to just be like “oh it should be on earlier” and move a pin, not considering the specifications for that decision.

So I want to implement what I think I want, and then if I don’t like it I’ll change it again.

Maybe AppDaemon is a good fit for you. It lets you program your automations in Python.

1 Like

I’ve heard some references to that though Python has been one of the most frustrating languages to figure out I’ve seen so far with everything being ambiguously loose typed and then mismatches of versions and no way to figure out fixing lost whitespace even worse than YAML…I’ll stop there before I end up on a 3 page rant.

Actually before 18:00 and after 18:00 intersect at exactly 18:00 (its the way the time boundary conditions are written)
But this makes no sense as a condition I agree

Eh, ok. I’m not a programmer, I don’t even work in the IT industry, but I learned enough Python to program fairly complex HA automations in about 2-3 months, but probably it’s different for an experienced programmer who is used to other programming languages.

How do you edit your files? I can not remember ever having an issue due to indendation/mix of spaces and tabs when using a proper Editor like Visual Studio Code.

Usually VIM or Notepadqq. It seems worst when trying to copy examples from the Internet and when you paste it ends up all on the left edge because they used CSS or something stupid to do the indents on the website and none of that translates.

Its also a PITA that settings are not persistent in HassOS so you have to rebuild your vimrc every time you do anything, on top of the nightmares with trying to guess what will REALLY work because the absurd “container everything” means you can’t test a program/script in the real environment and it inexplicably fails because oops mqtt_pub and all the libraries exists in the SSH container but not the who-knows-what-it-runs-in real container. That makes any minor issues with learning curves much more of a headache to debug with HomeAssistant.

Isn’t Visual Studio paid? I’ve only heard of that being used for .Net and C# at work, and it’s a big deal because of licensing costs, plus Windows (I run Linux)

I think Home Assistant Supervised on top of a generic Linux install would be more appropriate for a user with linux/programming background like you. Please be informed that if you do this, it will only be officially supported if the base OS is Debian and nothing else than HA is running on the machine.

Yes, Visual Studio is, but it’s not the same as Visual Studio Code. There’s even a Home Assistant Plugin for Visual Studio Code, which will auto-complete entities which is quite handy and other stuff as well.

Interesting…I was not aware there was an option to have a Vanilla Linux and still use add-ons and manage thru the web-UI. I would have much preferred that over the UEFI nightmare trying to make KVM/QEMU boot it properly. If it ever stops working I’ll have to see if I can set that up, I think it would be easier to manage. I bet that would solve some of my issues with LetsEncrypt too (I run my own DNS to autot-issue certs against my LAN machines automatically, but can’t get it working with HassOS since I can’t figure out how to make the script changes to the “container” addon thing)

It should be pretty easy to migrate to Vanilla Linux + HA Supervised (with add-ons etc.). Take a snapshot in Home Assistant, install HA Supervised on top of Vanilla Linux by following this for Debian 10, this for Ubuntu 18.04 or any of the other “HA Supervised” guides here, load your snapshot and you should be good to go.

1 Like

I’ll certainly have to look at that. I’d imagine it would also make managing multiple network interfaces easier (I have one for trusted-LAN and one non-Internet IoT untrusted devices)

Either Supervised or Home Assistant Core as a Python virtualenv installation, just-another-application running on a base Linux machine. If you’re familiar with Linux, that gives you complete freedom to do what you like on the rest of the machine. You do lose the Add-Ons & HACS systems though.

For me, who grew up with C at university (I even got a mention in preface of the 2nd ed of Deitel & Deitel’s C How To Program!) and that newfangled Java, Python is a breath of fresh air and my go-to (!) language of choice now. A decent .vimrc, banishing tabs to the abyss, and all is well with the world.

I’d second the AppDaemon recommendation as well: it’s a bit of a hurdle to set up on a Core machine, but allows much more complex systems to be set up, like my council bin schedule web scraping and deciding whether/when to run the immersion heater overnight based on the half-hourly tariff of Octopus Agile.

Here’s a suggestion: build yourself an ambient light sensor and switch the lights based on that, rather than time or weather reports which are “second-order” inputs. I’ve done this with our hall light: a Wemos D1 mini and a BH1750 sensor (GY-301 module) running ESPHome peeking out of a hole above the garage door reports the light level to HA, which then switches the light based on either house occupancy + sun state; or light level, sun azimuth angle (taking into account the west-facing windows driving a lower threshold in the morning) and an adjustment slider for fine-tuning. I tend to break things down into simpler elements, so I have a sensor for working out the switch threshold, a binary_sensor to indicate if the light should be on based only off that threshold, and then the automation (not shown) does the actual switching if a) house is occupied and binary_sensor stays on for 30s or off for 10m or b) house is unoccupied based purely on sun-up/down.

sensor:
  - platform: template
    sensors:
      hall_light_threshold:
        friendly_name: "Hall light threshold"
        unit_of_measurement: 'lx'
        entity_id:
          - sun.sun
          - input_number.light_threshold
        value_template: >-
          {% set azi = state_attr('sun.sun', 'azimuth')|float %}
          {% set adj = states('input_number.light_threshold')|float %}
          {# base setting 100lx at azi 80, 200lx at azi 270 #}
          {{ ((100 + ([0, azi-80]|max)/1.9) * (adj / 100))|int }}

binary_sensor:
  - platform: template
    sensors:
      hall_light:
        friendly_name: "Hall light should be on"
        entity_id:
          - sensor.hall_light_threshold
          - sensor.outside_illuminance
        value_template: "{{ states('sensor.outside_illuminance')|float < states('sensor.hall_light_threshold')|float  }}"

1 Like

Do you have a github repo or any other way to see your appdaemon apps? I’m really interested in how you tackle things in AppDaemon, the structure of your apps etc.

I don’t, but here’s the bin collection one as an example. Uses Python’s BeautifulSoup module to scrape the council website, which is a bit more complex than HA’s scrape sensor can cope with; and creates/updates the three timestamp sensors that show the next collection dates for my bins:

import hassapi as hass
from bs4 import BeautifulSoup
import datetime
from dateutil import parser
import requests

class BinCollection(hass.Hass):

    def initialize(self):
        """Sets up the uprn and the first run"""

        self.log("Bin collection app running")
        self.uprn = 'xxxxxx'
        self.run_in(self.refresh, 5)

    def refresh(self, kwargs):
        """Looks up the data, sets next run at 2am tomorrow"""

        url = "https://www.lichfielddc.gov.uk/homepage/6/" \
              f"bin-collection-dates?uprn={self.uprn}"
        page = requests.get(url)

        if page.status_code != 200:
            return None

        soup = BeautifulSoup(page.text, 'html.parser')

        bh3s = soup.find_all('h3', class_="bin-collection-tasks__heading")
        bpds = soup.find_all('p', class_="bin-collection-tasks__date")

        for i in range(len(bpds)):
            bin_colour = bh3s[i].contents[1].split(' ')[0].lower()
            bin_date = parser.parse(bpds[i].contents[0]).strftime('%Y-%m-%d')
            self.log(f"{bin_date}: {bin_colour} bin")
            self.set_state(f"sensor.{bin_colour}_bin",
                           state=bin_date,
                           attributes={'device_class': 'timestamp'})

        tomorrow = datetime.datetime.today() + datetime.timedelta(days=1)
        next_run = tomorrow.replace(hour=2, minute=0, second=0, microsecond=0)

        self.log(f"Scheduling next run for {next_run.isoformat()}")

        self.run_at(self.refresh, next_run)
entities:
  - entity: sensor.black_bin
    icon: 'mdi:trash-can-outline'
    name: Black
  - entity: sensor.blue_bin
    icon: 'mdi:recycle'
    name: Blue
  - entity: sensor.brown_bin
    icon: 'mdi:flower'
    name: Brown
style: |-
  ha-card { border-radius: 8px; }
  div.entity:nth-of-type(1) { color: black; }
  div.entity:nth-of-type(2) { color: blue; }
  div.entity:nth-of-type(3) { color: brown; }
title: Bin collections
type: glance

image

1 Like

I finally found myself needing to re-visit this and did a test to “prove” it by setting up several automations and observing which fired.

I’ll let the results speak for themselves…

  - alias: 'Test event - 1'
    trigger:
    - platform: time
      at: '20:00:00'
    condition:
    - condition: time
      after: '20:00:00'
      before: '20:00:00'
    action:
      service: notify.telegram_send
      data:
        message: 'Test event 1 - 20:00:00 - 20:00:00'

  - alias: 'Test event - 2'
    trigger:
    - platform: time
      at: '20:00:00'
    condition:
    - condition: time
      after: '20:00:00'
      before: '20:00:01'
    action:
      service: notify.telegram_send
      data:
        message: 'Test event 2 - 20:00:00 - 20:00:01'

  - alias: 'Test event - 3'
    trigger:
    - platform: time
      at: '20:00:00'
    condition:
    - condition: time
      after: '19:59:59'
      before: '20:00:00'
    action:
      service: notify.telegram_send
      data:
        message: 'Test event 3 - 19:59:59 - 20:00:00'

  - alias: 'Test event - 4'
    trigger:
    - platform: time
      at: '20:00:00'
    condition:
    - condition: time
      after: '19:59:59'
      before: '20:00:01'
    action:
      service: notify.telegram_send
      data:
        message: 'Test event 4 - 19:59:59 - 20:00:01'

Result:

Home Assistant, [4/2/22 8:00 PM]
Test event 4 - 19:59:59 - 20:00:01

Home Assistant, [4/2/22 8:00 PM]
Test event 1 - 20:00:00 - 20:00:00

Home Assistant, [4/2/22 8:00 PM]
Test event 2 - 20:00:00 - 20:00:01
2 Likes

@mmiller7 Did you ever work out whether before and after are exclusive or inclusive?

So, if you did this:

  - choose:
      - conditions:
          - condition: time
            after: "00:00:00"
            before: "01:00:00"
        sequence:
          - service: scene.turn_on
            target:
              entity_id: scene.master_bedroom_00_00_00_59
            metadata: {}
      - conditions:
          - condition: time
            after: "01:00:00"
            before: "02:00:00"
        sequence:
          - service: scene.turn_on
            target:
              entity_id: scene.master_bedroom_01_00_01_59
            metadata: {}

would either of those two scenes be activated at exactly 01:00:00, and if not, what would be the way to efficiently make one of them activate at exactly 01:00:00 ?

Apparently “choose” is a new thing I haven’t seen yet…but you could do a similar test I’d imagine to work out what it does

@mmiller7 conditions work the same everywhere, so the result would be the same for your tests. It doesn’t matter whether it’s an automation’s condition, a choose or an if.

@jsh the answer is right before your post: after is inclusive and before is exclusive. But, because of the ambiguity and readability in some cases, I just opt for a template condition to make it explicit (using the various comparison operators).

1 Like

To provide an example, this is completely unambiguous to me:

{% set within_window = today_at('03:30') <= now() <= today_at('11:30') %}

This looks to me like after is inclusive and before is exclusive.

Meaning a condition of “after 20:00” will be true at 20:00, but if you trigger an event at 20:01 and you have “before 20:01”, it will be false.

EDIT: Looks like @parautenbach clarified this in an earlier post.

1 Like