Monitoring working hours at home, based on device_tracker

My wife is working from home and I use device_tracker to monitor her laptop on our wifi-network. I can disable certain automations based on the condition she’s at home and also use it to track her working hours. Her laptop is not 100% time online, so there might be caps in device_tracker status here and there. Caps are usually less than 15mins (except lunch break), so I had to filter small “breaks” out.

First I did a history stats for the time laptop is offline:

sensors.yaml

 - platform: history_stats
    name: Laptop in sleep
    entity_id: device_tracker.xxxxx
    state: 'not_home'
    type: time
    start: '{{ as_timestamp(states.device_tracker.xxxxx.last_changed) }}'
    end: '{{ now() }}'

Then I created template sensor for monitoring if her laptop has been offline for at least 15 mins:

sensors.yaml

  - platform: template
    sensors:
      wife_working_status:
        friendly_name: "Wife is working"
        value_template: >-
          {% if (states('sensor.laptop_in_sleep') | float > 0.25) %}
            Wife free
          {% else %}
            Wife busy working
          {% endif %}

With the following history_stats sensor I count the working hours per day:

- platform: history_stats
    name: Working hours counter
    entity_id: sensor.wife_working_status
    state: 'Wife busy working'
    type: time
    start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}'

It’s working fine, but because of the 15min delay I have to wait before I can tell she really has finished working, there’s always 15min extra time in the “working hours” -counter.

How can I subtract this 15mins (or actually 30mins, because lunch break creates another 15mins)?

EDIT: Added the “working hours counter” -sensor as requested.
For future improvements: It would be nice to have some weekly stats about the working hours, like:
Mon: 7.5h
Tue: 8.3h

Week 40 total: 40h

Where is the config for the “working hours counter”?

It was missing, but I edited the original post to include it.
I came up with one possible solution: Creating another sensor that will subtract 30min from the “working hours sensor”. Not ideal though…

I did the same based on location and tracking when im at work. This also filters out my lunch break as ill be outside the zone specified.

In order to get the statistics about the weekdays i added 5 sensors (one for each day) and an extra one for the whole week with the following config:

  - platform: history_stats
    name: work monday
    entity_id: person.marcus
    state: 'Work'
    type: time
    start: '{{ as_timestamp( now().replace(hour=0).replace(minute=0).replace(second=0) ) - now().weekday() * 86400 }}'
    duration: '24:00'
  - platform: history_stats
    name: work tuesday
    entity_id: person.marcus
    state: 'Work'
    type: time
    start: '{{ as_timestamp( now().replace(hour=0).replace(minute=0).replace(second=0) ) + (1 - now().weekday()) * 86400 }}'
    duration: '24:00'
  - platform: history_stats
    name: work wednesday
    entity_id: person.marcus
    state: 'Work'
    type: time
    start: '{{ as_timestamp( now().replace(hour=0).replace(minute=0).replace(second=0) ) + (2 - now().weekday()) * 86400 }}'
    duration: '24:00'
  - platform: history_stats
    name: work thursday
    entity_id: person.marcus
    state: 'Work'
    type: time
    start: '{{ as_timestamp( now().replace(hour=0).replace(minute=0).replace(second=0) ) + (3 - now().weekday()) * 86400 }}'
    duration: '24:00'
  - platform: history_stats
    name: work friday
    entity_id: person.marcus
    state: 'Work'
    type: time
    start: '{{ as_timestamp( now().replace(hour=0).replace(minute=0).replace(second=0) ) + (4 - now().weekday()) * 86400 }}'
    duration: '24:00'
  - platform: history_stats
    name: work week test
    entity_id: person.marcus
    state: 'Work'
    type: time
    start: '{{ as_timestamp( now().replace(hour=0).replace(minute=0).replace(second=0) ) - now().weekday() * 86400 }}'
    end: '{{ now() }}'

I also summarize them all in NodeRed and output the values (including overtime calculation etc) into a custom sensor (created via the HTTP API).

One Problem i found with the History Stats Sensor:
Everything works great for the first week. But on week 2 on Monday the Tue - Fri Sensors do not update to the correct (UNKNOWN) value, but keep the value of last week due to the date being in the future. A Restart helps in clearing them and getting to the correct values. I already opened an issue on Github for that component.

Thanks for the daily working hour counter, sounds like just what I’m looking for.
My first problem still remains unsolved.

Let me take a step back from my initial approach. I’ll try to express what I really want to do…
I would like to have a sensor reading the first change of the day from ‘Away’ to ‘Home’, and the last change from ‘Home’ to ‘Away’ and count the hours/minutes in between. It’s never one continuing state of device being ‘Home’, but something like in the picture below:


There’s an attribute ‘last_changed’ to catch the last change, but I don’t know how to catch the first change of the day.

First you will need to decide what constitutes the “first change of the day”.

The first one after midnight? or the first one around a specific time of day?

Either way once you decide on that you’ll have to have an automation to set the value of an input_datetime.

assuming its the first time after midnight then try:

input_datetime:
  first_away_time:
    name: First Away Time
    has_date: true
    has_time: true

input_boolean:
  first_time_set:
    name: First Time Has Been Set
  
automation:
  - alias: set first away time
    trigger:
      - platform: state
        entity_id: device_tracker.x5cg63723rr00e3
        to: 'not_home'
    condition:
      - condition: state
        entity_id: input_boolean.first_time_set
        state: 'off'
    action:
      - delay: '00:00:10'
      - service: input_datetime.set_datetime
        entity_id: input_datetime.first_away_time
        data_template:
          time: "{{ states.device_tracker.x5cg63723rr00e3.last_changed }}"
      - service: input_boolean.turn_on
        entity_id: input_boolean.first_time_set
  - alias: reset first time set boolean at midnight
    trigger:
      - platform: time
        at: '00:00:00'
    action:
      - service: input_boolean.turn_off
        entity_id: input_boolean.first_time_set

Thanks a lot for your help!
For the past couple of days I’ve been busy trying to make this all work. Finally I have some results to share.

I’m catching the monitored device changing state from “not_home” to “home” and marking that time as a work starting time. Every time the device changes to “not_home”, input_datetime for that specific day is updated (time elapsed between last ‘not_home’ time and ‘working_start_time’). I’m planning to limit the “last not_home” somehow to prevent false triggers, e.g. if laptop is used in the evening for non-work related things.
This could be achieved by history_stats sensor by checking how long the laptop has been continuously in “not_home” state. If this state exceeds, let’s say 60mins, we can turn off “input_boolean.set_working_started” and call it a day.

input_boolean:
  set_working_started:
    name: Working has started
    
input_datetime:
  working_start_time:
    name: Working start time
    has_date: true
    has_time: true
      
  working_time_0_monday:
    name: Working time Monday
    has_date: false
    has_time: true
  working_time_1_tuesday:
    name: Working time Tuesday
    has_date: false
    has_time: true
  working_time_2_wednesday:
    name: Working time Wednesday
    has_date: false
    has_time: true
  working_time_3_thursday:
    name: Working time Thursday
    has_date: false
    has_time: true
  working_time_4_friday:
    name: Working time Friday
    has_date: false
    has_time: true
      
automation:
  - alias: Set working start time
    trigger:
      - platform: state
        entity_id: device_tracker.x5cg63723rr00e3
        to: 'home'
    condition:
      - condition: state
        entity_id: input_boolean.set_working_started
        state: 'off'
    action:
      - service: input_boolean.turn_on
        entity_id: input_boolean.set_working_started
      - service: input_datetime.set_datetime
        data_template:
          entity_id: input_datetime.working_start_time
          date: '{{ (as_timestamp(states.device_tracker.x5cg63723rr00e3.last_changed) | timestamp_custom("%Y-%m-%d", true)) }}'
          time: '{{ (as_timestamp(states.device_tracker.x5cg63723rr00e3.last_changed) | timestamp_custom("%H:%M:%S", true)) }}'
        
  - alias: Reset working start time boolean at midnight
    trigger:
      - platform: time
        at: '00:00:00'
    action:
      - service: input_boolean.turn_off
        entity_id: input_boolean.set_working_started
        
  - alias: Update working time on weekdays
    trigger:
      - platform: state
        entity_id: device_tracker.x5cg63723rr00e3  
        to: 'not_home'
    condition:
      - condition: state
        entity_id: input_boolean.set_working_started
        state: 'on'
    action:
      - service: input_datetime.set_datetime
        data_template:
          entity_id: > 
            {% if (now().weekday()) == 0 %}
              input_datetime.working_time_0_monday
            {% elif (now().weekday()) == 1 %}
              input_datetime.working_time_1_tuesday
            {% elif (now().weekday()) == 2 %}
              input_datetime.working_time_2_wednesday
            {% elif (now().weekday()) == 3 %}
              input_datetime.working_time_3_thursday
            {% elif (now().weekday()) == 4 %}
              input_datetime.working_time_4_friday
            {% endif %}
          time: >
            {{ (as_timestamp(states.device_tracker.x5cg63723rr00e3.last_changed) - as_timestamp(states('input_datetime.working_start_time'))) | timestamp_custom("%H:%M:%S", false) }}

Hi,
Great work.
For some reason i get The value zero on every day.
My history stat sensor without The specificera days work though. Any thoughts on why its not working for me?

Do you possibly have the history retention set to 24h?