Keep track of the average time that something happens - per day of the week, weekend, workday, or week

Average Event Time

Home Assistant can do many things using statistics. What isn’t easy to do is to calculate statistics based the time that something has happened, specific to the day of the week.

I created a macro that allows you to do just that. It helps you account for many exceptions that happen when measuring average time. This template started small, but didn’t stay that way for long. That is why I thought I should share it as a HACS repository you can use.

The macro keeps track of the minumum, average and maximum times of events for each day of the week, for weekend days, workdays or the whole week. It can tell you what time is expected for today or tomorrow based on these statistics.

While it has many options it is quite simple to set up if you use the below examples.

Example usecase

An example of what you can use this for is to keep track of how late you usually go to bed and what time you normally wake up. The averages can then for instance be used to determine when to start heating, and when to turn off the heating for the night. By using this you’ll have built a simple, lightweigt self learning heating system.

Features

  • The statistics are calculated over a period of a number of weeks, starting from when you create the template. The default is 4 weeks. There’s no need to worry about purge settings of the recorder. As soon as you create a sensor using one of the examples below, it starts tracking the information in an attribute. As long as that is not lost, you’ll have all you need.

  • Because events can sometimes happen multiple times a day, the sensor can be configured to only remember the first occurrence of that day, or the last one. At maximum only one event is recorded per day that is used for the statistics, the others are ignored. This will help you keep the times as accurate as possible.

  • It is possible to take into account that sometimes things happen after midnight. Especially for late events, you would not want those events to be considerd for the next day. If needed you can set it so that something that happens in the early hours is considered to belong to the day before. If you go to bed late, the time will still averaged the right way. It also affects the definition of a weekend. For late events, the weekend is considered to be friday evening and saterday evening, instead of on saturday and sunday.

  • If you use a for statement in your trigger to detect an event then you can compensate for the delay and record the actual time that the event started. The compensation must of course match the delay used in the for statement of the initial trigger.

  • If you wish, events that didn’t happen on some day can be kept blank and ignored for the statistics. You can also opt to keep an older value in the statistics instead. One caveat is that it may not always be the least old one that is kept if you often miss events on that weekday. That is a result of the way the data is stored.

  • You can set separate defaults for work days and weekends for when you do not have any measured events yet.

Installation

To use this macro, add this repository as a custom template repository to HACS and install it. or download the avg_event_time.jinja from it and place the files into your config\custom_templates directory.

The easiest way to start setting up sensors is to copy the examples below and adjust them to your needs.

How to use this

The macro provided by this template builds a data structure that is meant to be kept in the attribute of a sensor. The parameters tell the macro what to do. The best way to use this is to create a trigger based template sensor to record the attribute, and have the state report the time you want to use. See the exmaples below. The triggers for the template sensor are used to determine when to update and when to record times.

You can use any trigger to record the time, but it is advised to use an event trigger. That way you are able to write complex automations to trigger the recording of the time. You’ll have full control over when it happens. One reason to do so is that you could for instace put a time constraint on when the events are recorded in order to prevent wrong readings. For instance, if you have a button to record you are awake, you could choose to ignore the button if it is pressed after noon. It is hard to do that right in a trigger based template sensor.

Examples

Be sure to install the macro before adding this to your configuration! See installation instructions.

Waking up

A sensor to record the time you normally wake up, to put in templates.yaml. The state is the average time you usually wake up today. The statistics are kept in the attributes:

- trigger:
    - trigger: event
      id: event
      event_type: "record_awake"
    - trigger: time
      at: "00:00:00"
      id: begin
    - trigger: time
      at: "23:59:59"
      id: end
    - trigger: homeassistant
      id: start
      event: start
    - trigger: event
      event_type: event_template_reloaded
      id: "reload"
    - trigger: event
      id: reset
      event_type: "reset_awake"
  action:
      variables:
        stats: >- 
          {% from 'avg_event_time.jinja' import avg_event_time %}
          {{ avg_event_time(state_attr('sensor.usual_time_awake','stats'), trigger.id,
          first=True, use_we_dft='9:00', use_wd_dft='06:30') | from_json }}
  sensor:
    - name: Usual time awake
      unique_id: sensor.usual_time_awake
      state: "{{ stats.today.avg | as_datetime(None) }}"
      device_class: timestamp
      attributes:
        stats: "{{ stats }}"

An action to record when you wake up, for use in an automation:

    - event: record_awake
      event_data: {}

Going to sleep

A sensor to record the time when you normally go to bed, to put in templates.yaml. The state is the average time you usually go to sleep today. The statistics are kept in the attributes:

- trigger:
    - trigger: event
      id: event
      event_type: "record_asleep"
    - trigger: time
      at: "04:00:00"
      id: begin
    - trigger: time
      at: "03:59:55"
      id: end
    - trigger: homeassistant
      id: start
      event: start
    - trigger: event
      event_type: event_template_reloaded
      id: "reload"
    - trigger: event
      id: reset
      event_type: "reset_asleep"
  action:
      variables:
        stats: >- 
          {% from 'avg_event_time.jinja' import avg_event_time %}
          {{ avg_event_time(state_attr('sensor.usual_time_asleep','stats'), trigger.id,
          first=False, night=True, use_we_dft='23:30', use_wd_dft='22:30') | from_json }}
  sensor:
    - name: Usual time asleep
      unique_id: sensor.usual_time_asleep
      state: "{{ stats.today.avg | as_datetime(None) }}"
      device_class: timestamp
      attributes:
        stats: "{{ stats }}"

An action to record when you go to sleep, for use in an automation:

  - event: record_asleep
    event_data: {}

Generate markdown for the statistics of multile entities (set labels to None to leave values away):

  {% from 'avg_event_time.jinja' import avg_event_time_markdown %}
  {{ avg_event_time_markdown(
      [ 
        [ 'sensor.usual_time_awake', 'stats', 'Wake' ] ,
        [ 'sensor.usual_time_asleep', 'stats', 'Sleep' ] 
      ], 
      title = 'Me', 
      stat = [ 'min', 'avg', 'max' ], 
      wd = [ 'Mondays', 'Tuesdays', 'Wednesdays', 'Thursdays', 'Fridays', 'Saturdays', 'Sundays',
             'Today', 'Workdays', 'Weekends', 'All days' ] ) }}
2 Likes

interesting.

could you possibly share an example output?

The example sensor itself is set to just output a datetime for the average occurrence time today. If you’re interested in the attribute that you can get the information from then this is what it looks like. I left out the table with the raw data for brevity, That is a two dimensional array with counts in seconds.


{
  "last_trigger_date": "2025-03-03",
  "event_time_today": null,
  "next": {
    "min": "2025-03-04T22:57:00+01:00",
    "avg": "2025-03-04T23:19:00+01:00",
    "max": "2025-03-04T23:44:00+01:00"
  },
  "today": {
    "min": "2025-03-04T22:57:00+01:00",
    "avg": "2025-03-04T23:19:00+01:00",
    "max": "2025-03-04T23:44:00+01:00"
  },
  "tomorrow": {
    "min": "2025-03-05T23:01:00+01:00",
    "avg": "2025-03-05T23:23:00+01:00",
    "max": "2025-03-05T23:54:00+01:00"
  },
  "week": {
    "min": "22:41",
    "avg": "23:40",
    "max": "01:23"
  },
  "weekend": {
    "min": "23:24",
    "avg": "23:57",
    "max": "00:42"
  },
  "workday": {
    "min": "22:41",
    "avg": "23:33",
    "max": "01:23"
  },
  "weekday": [
    {
      "min": "22:41",
      "avg": "23:37",
      "max": "01:23"
    },
    {
      "min": "22:57",
      "avg": "23:19",
      "max": "23:44"
    },
    {
      "min": "23:01",
      "avg": "23:23",
      "max": "23:54"
    },
    {
      "min": "23:18",
      "avg": "23:36",
      "max": "00:03"
    },
    {
      "min": "23:39",
      "avg": "00:01",
      "max": "00:36"
    },
    {
      "min": "23:24",
      "avg": "23:54",
      "max": "00:42"
    },
    {
      "min": "22:59",
      "avg": "23:43",
      "max": "00:33"
    }
  ]
}

ps. If you wonder why I did things so late on workdays: I built this in my vacation. Plus I needed to test late nights too. :slight_smile: Below is a sample of the markup it can generate.

Wake min Wake avg Wake max Sleep min Sleep avg Sleep max
Today 06:55
Workdays 06:36 07:33 09:24 22:41 23:33 01:23
Weekends 08:01 08:45 10:03 23:24 23:57 00:42
All days 06:36 07:55 10:03 22:41 23:40 01:23
Mondays 06:41 07:38 09:23 22:41 23:37 01:23
Tuesdays 06:36 07:26 09:24 22:57 23:19 23:44
Wednesdays 06:49 07:33 08:58 23:01 23:23 23:54
Thursdays 06:42 07:32 09:12 23:18 23:36 00:03
Fridays 06:49 07:36 09:10 23:39 00:01 00:36
Saturdays 08:01 08:18 08:31 23:24 23:54 00:42
Sundays 08:37 09:05 10:03 22:59 23:43 00:33
1 Like

cool! I see some uses for this! thanks for the output example!!

it got me thinking, of a bit of an inverse of this … taking a measured values at a specific time (eg each hour) and making some statistics on that value could drive some forecasting but based on values changes and statistics not state changes in times and statistics of the time. I took a quick look at the code and see first that such an inverse would need completely new code i think but also I see you managed all this interesting stuff in Jinja, inspiring!

thanks for sharing all this!!

1 Like