How do stop read only the last 5 minutes of email and stop the email content imap sensor being active

I currently use :

Sensor for Dominos Order Confirmation

  • platform: imap_email_content
    name: Dominos
    server: imap.gmail.com
    #search: SUBJECT dominos pizza order confirmation
    port: 993
    username: !secret x_google
    password: !secret x_google_pw
    folder: Inbox
    senders:
    • [email protected]
      value_template: >-
      {% if ‘dominos pizza order confirmation’ in body %}
      order
      {% else %}
      no_order
      {% endif %}

It starts out as unknown, until it receives an email. Then once it receives an email (that matches the value_template, it goes into order, otherwise it goes in no_order. All fine.

Then it goes to states of 13minutes unknown (i guess a gmail thing) and then back to order again (if a pizza was ordered.

How can i make read email in the past 5 minutes or so. Or make a imap mail READ…

sensor: 
  - platform: imap_email_content
    name: Dominos
    server: imap.gmail.com
    port: 993
    username: !secret x_google
    password: !secret x_google_pw
    folder: Inbox
    senders:
      - [email protected]
    value_template: >
      {% if ‘dominos pizza order confirmation’ in body and
      (now() - (strptime(date, "%a, %d %b %Y %H:%M:%S %z")) < timedelta(minutes=5)) %}
        order
      {% else %}
        no_order
      {% endif %}
1 Like

Wow, nice!

So i’m currently using this sensor in addition:

# Sensor for School New Email
  - platform: imap_email_content
    name: School
    server: imap.gmail.com
    #search: BODY accident
    port: 993
    username: !secret x_google
    password: !secret x_google_pw
    folder: Inbox
    senders:
      - [email protected]
      value_template: >-
      {% if 'accident' or 'unwell' in body and 
      (now() - (strptime(date, "%a, %d %b %Y %H:%M:%S %z")) < timedelta(minutes=5)) %}
        accident
      {% else %}
        no_accident
      {% endif %}

I got the email yesterday but i missed it as it didn’t include ‘unwell’, then i changed the sensor to also include the above (e.g. and ‘unwell’) then it triggered to accident again at 21:59.

I have no idea why it did that, should it not only parse the last 5 minutes ?

The issue is the construction of your if statement. Strings by themselves are considered true, so when you start your template with if 'accident' or 'unwell' in body...., that get rendered as if TRUE or ('unwell' in body') so it’s always true.

To test multiple terms, you can use multiple in tests i.e.

{% if ('accident' in body or 'illness' in body) and ...

or use the search() function instead, using | between search terms:

    ....
    senders:
      - [email protected]
    value_template: >-
      {% if body is search('accident | illness') and 
      (now() - (strptime(date, "%a, %d %b %Y %H:%M:%S %z")) < timedelta(minutes=5)) %}
        accident
      {% else %}
        no_accident
      {% endif %}

Understood, i will try with search. But even if the string is true, should it not only become true when there is an email from the school in the last 5 minutes ?

No, because the ‘and’ operator has precedence over the ‘or’. So, assuming the email is older than 5 minutes and neither word is in the body of the email, if we show it in steps it renders as follows:

{% if 'accident' or 'unwell' in body and 
(now() - (strptime(date, "%a, %d %b %Y %H:%M:%S %z")) < timedelta(minutes=5)) %}

Step 1: Render each clause to TRUE or FALSE

{% if  TRUE or FALSE and FALSE %}

Step 2: Since "AND" has precendence, combine those clauses

{% if TRUE or FALSE %}

Step 3: Return the result of the "OR"

TRUE

You can overrule this by using parentheses () around the clauses you want to combine as I did in the example using multiple in tests.

In the first example where i use:

{% if 'accident' or 'unwell' in body and 
(now() - (strptime(date, "%a, %d %b %Y %H:%M:%S %z")) < timedelta(minutes=5)) %}

So i understand the a string is always true, and i use ‘accident’ OR ‘unwell’ in body, if the sensor is still active, that is because the AND operator has precedence. But the code after the AND operator only reads only the last 5 minutes right ?

Just in case i get my parentheses mixed up, i think i needed one more ) in your example code? (it didn’t work for me otherwise) i.e. |illness’))

    ....
    senders:
      - [email protected]
    value_template: >-
      {% if (body is search('unwell | illness')) and 
      (now() - (strptime(date, "%a, %d %b %Y %H:%M:%S %z")) < timedelta(minutes=5)) %}
        accident
      {% else %}
        no_accident
      {% endif %}

Many thanks, this is really helpful.

The clause after the and basically asks the question “Was the latest email from the defined senders sent less than 5 minutes ago?”

Make sure you put ‘accident’ back in your template… I used ‘unwell’ in my tests and forgot to change it in my answer originally.

Cool thanks!

One more question if you wouldn’t mind, does the sensor go to unavailable sometimes? Obviously it is unavailable until it gets an email, but after that it should either be accident or no_accident right?

I have a feeling sometimes gmail looses connection and it goes back to unknown.

Because the IMAP Content integration is based on a Cloud Push mechanism, you can expect the state of the sensor will be ‘unknown’ after every restart of HA or anytime the integration is reloaded. Once an email is received from the sender the state will change to the appropriate value based on the template.

There have been a few times I have noticed that my own IMAP content sensors don’t go back to “unknown” after restart, but I have no idea why that was.

1 Like