Skip to content

Instantly share code, notes, and snippets.

@khenderick
Last active May 8, 2025 01:53
Show Gist options
  • Save khenderick/1b46d7db50253813ef2a71ee37b5e242 to your computer and use it in GitHub Desktop.
Save khenderick/1b46d7db50253813ef2a71ee37b5e242 to your computer and use it in GitHub Desktop.
Kids screentime
- id: reset_extra_screen_time
alias: "Reset extra screen time"
mode: single
max_exceeded: silent
trigger:
- platform: time
at: "00:00:00"
condition:
condition: time
weekday:
- mon
action:
- service: input_number.set_value
target:
entity_id: input_number.child_1_extra_screen_time
data:
value: >-
{% if states('input_number.child_1_extra_screen_time') | int(0) >= 0 %}
{{ min(states('input_number.child_1_extra_screen_time') | int(0), states('sensor.child_1_weekly_screen_time_remaining') | int(0)) }}
{% else %}
{{ min(0, 0 + states('input_number.child_1_extra_screen_time') | int(0) + states('sensor.weekly_screen_time') | int(0)) }}
{% endif %}
- service: input_number.set_value
target:
entity_id: input_number.child_1_correction_screen_time
data:
value: 0
- service: input_number.set_value
target:
entity_id: input_number.child_2_extra_screen_time
data:
value: >-
{% if states('input_number.child_2_extra_screen_time') | int(0) >= 0 %}
{{ min(states('input_number.child_2_extra_screen_time') | int(0), states('sensor.child_2_weekly_screen_time_remaining') | int(0)) }}
{% else %}
{{ min(0, 0 + states('input_number.child_2_extra_screen_time') | int(0) + states('sensor.weekly_screen_time') | int(0)) }}
{% endif %}
- service: input_number.set_value
target:
entity_id: input_number.child_2_correction_screen_time
data:
value: 0
- id: warn_screen_time_almost_used
alias: "Warn screen time almost used"
mode: queued
max_exceeded: silent
trigger:
- platform: template
value_template: "{{ states('sensor.child_1_screen_time_today') | float(0) * 60 + 10 >= 120 }}"
id: "child_1_day"
- platform: template
value_template: "{{ states('sensor.child_2_screen_time_today') | float(0) * 60 + 10 >= 120 }}"
id: "child_2_day"
- platform: template
value_template: >
{{ states('sensor.child_1_weekly_screen_time_threshold') | int(-1) > 0 and
states('sensor.child_1_screen_time_this_week') | int(0) + 10 >= states('sensor.child_1_weekly_screen_time_threshold') | int(0) }}
id: "child_1_week"
- platform: template
value_template: >
{{ states('sensor.child_2_weekly_screen_time_threshold') | int(-1) > 0 and
states('sensor.child_2_screen_time_this_week') | int(0) + 10 >= states('sensor.child_2_weekly_screen_time_threshold') | int(0) }}
id: "child_2_week"
variables:
names:
child_1_day:
message: "Child 1, je hebt bijna 2 uur van je schermtijd gebruikt."
child_1_week:
message: "Child 1, je hebt bijna je wekelijkse schermtijd gebruikt."
child_2_day:
message: "Child 2, je hebt bijna 2 uur van je schermtijd gebruikt."
child_2_week:
message: "Child 2, je hebt bijna je wekelijkse schermtijd gebruikt."
action:
- variables:
message: "{{ names[trigger.id].message }}"
- service: script.cloud_say
data:
entity_id: media_player.muse_living
message: "{{ message }}"
volume: 0.5
language: "NL"
- id: warn_weekly_screen_time
alias: "Warn weekly screen time"
mode: queued
max_exceeded: silent
trigger:
- platform: template
value_template: "{{ states('sensor.child_1_screen_time_this_week') | int(0) >= states('sensor.child_1_weekly_screen_time_threshold') | int(-1) > 0 }}"
id: "child_1"
- platform: template
value_template: "{{ states('sensor.child_2_screen_time_this_week') | int(0) >= states('sensor.child_2_weekly_screen_time_threshold') | int(-1) > 0 }}"
id: "child_2"
variables:
names:
child_1:
pronunciation: "Child 1"
name: "Child 1"
child_2:
pronunciation: "Child 2"
name: "Child 2"
action:
- variables:
pronunciation: "{{ names[trigger.id].pronunciation }}"
name: "{{ names[trigger.id].name }}"
- service: script.cloud_say
data:
entity_id: media_player.muse_living
message: "{{ pronunciation }}, je schermtijd voor deze week is op."
volume: 0.5
language: "NL"
- service: notify.own_devices
data:
title: "Screen time"
message: "{{ name }} is out of screen time"
data:
notification_icon: "mdi:television-off"
- id: notify_screen_time
alias: "Notify screen time"
mode: queued
max_exceeded: silent
variables:
names:
input_boolean.child_1_screen_time_active:
pronunciation: "Child 1"
name: "child_1"
input_boolean.child_2_screen_time_active:
pronunciation: "Child 2"
name: "child_2"
numbers: "{{ {0: 'nul', 1: 'een', 2: 'twee', 3: 'drie', 4: 'vier', 5: 'vijf', 6: 'zes', 7: 'zeven', 8: 'acht', 9: 'negen', 10: 'tien', 11: 'elf', 12: 'twaalf', 13: 'dertien', 14: 'veertien', 15: 'vijftien', 16: 'zestien', 17: 'zeventien', 18: 'achttien', 19: 'negentien', 20: 'twintig', 21: 'eenentwintig', 22: 'tweeรซntwintig', 23: 'drieรซntwintig', 24: 'vierentwintig', 25: 'vijfentwintig', 26: 'zesentwintig', 27: 'zevenentwintig', 28: 'achtentwintig', 29: 'negenentwintig', 30: 'dertig', 31: 'eenendertig', 32: 'tweeรซndertig', 33: 'drieรซndertig', 34: 'vierendertig', 35: 'vijfendertig', 36: 'zesendertig', 37: 'zevenendertig', 38: 'achtendertig', 39: 'negenendertig', 40: 'veertig', 41: 'eenenveertig', 42: 'tweeรซnveertig', 43: 'drieรซnveertig', 44: 'vierenveertig', 45: 'vijfenveertig', 46: 'zesenveertig', 47: 'zevenenveertig', 48: 'achtenveertig', 49: 'negenenveertig', 50: 'vijftig', 51: 'eenenvijftig', 52: 'tweeรซnvijftig', 53: 'drieรซnvijftig', 54: 'vierenvijftig', 55: 'vijfenvijftig', 56: 'zesenvijftig', 57: 'zevenenvijftig', 58: 'achtenvijftig', 59: 'negenenvijftig', 60: 'zestig', 61: 'eenenzestig', 62: 'tweeรซnzestig', 63: 'drieรซnzestig', 64: 'vierenzestig', 65: 'vijfenzestig', 66: 'zesenzestig', 67: 'zevenenzestig', 68: 'achtenzestig', 69: 'negenenzestig', 70: 'zeventig', 71: 'eenenzeventig', 72: 'tweeรซnzeventig', 73: 'drieรซnzeventig', 74: 'vierenzeventig', 75: 'vijfenzeventig', 76: 'zesenzeventig', 77: 'zevenenzeventig', 78: 'achtenzeventig', 79: 'negenenzeventig', 80: 'tachtig', 81: 'eenentachtig', 82: 'tweeรซntachtig', 83: 'drieรซntachtig', 84: 'vierentachtig', 85: 'vijfentachtig', 86: 'zesentachtig', 87: 'zevenentachtig', 88: 'achtentachtig', 89: 'negenentachtig', 90: 'negentig', 91: 'eenennegentig', 92: 'tweeรซnnegentig', 93: 'drieรซnnegentig', 94: 'vierennegentig', 95: 'vijfennegentig', 96: 'zesennegentig', 97: 'zevenennegentig', 98: 'achtennegentig', 99: 'negenennegentig'} }}"
trigger:
- platform: state
entity_id: input_boolean.child_1_screen_time_active
to: null
- platform: state
entity_id: input_boolean.child_2_screen_time_active
to: null
action:
- variables:
pronunciation: "{{ names[trigger.entity_id].pronunciation }}"
name: "{{ names[trigger.entity_id].name }}"
today: "{{ (states('sensor.' + name + '_screen_time_today') | float(0) * 60) | int(0) }}"
today_hours: "{{ today // 60 }}"
today_minutes: "{{ today % 60 }}"
remaining: "{{ states('sensor.' + name + '_weekly_screen_time_remaining') | int(0) }}"
remaining_hours: "{{ remaining // 60 }}"
remaining_minutes: "{{ remaining % 60 }}"
- service: script.cloud_say
data:
entity_id: media_player.muse_living
message: >
{{ pronunciation }},
{%- if remaining > 0 -%}
je schermtijd is {{ 'gestart' if is_state(trigger.entity_id, 'on') else 'gestopt' }}.
{%- endif -%}
{%- if today > 0 -%}
Je hebt vandaag al
{%- if today_hours > 0 -%}
{{ ' ' }}{{ numbers[today_hours] }} uur
{%- endif -%}
{%- if today_hours > 0 and today_minutes > 0 -%}
{{ ' ' }}en
{%- endif -%}
{%- if today_minutes > 0 -%}
{{ ' ' }}{{ numbers[today_minutes] }} min{{ 'uut' if today_minutes == 1 else 'uten' }}
{%- endif %} gebruikt.
{%- endif -%}
{{ ' ' }}Je hebt
{%- if remaining <= 0 -%}
{{ ' ' }}geen tijd meer over deze week.
{%- else -%}
{{ ' ' }}nog
{%- if remaining_hours > 0 -%}
{{ ' ' }}{{ numbers[remaining_hours] }} uur
{%- endif -%}
{%- if remaining_hours > 0 and remaining_minutes > 0 -%}
{{ ' ' }}en
{%- endif -%}
{%- if remaining_minutes > 0 -%}
{{ ' ' }}{{ numbers[remaining_minutes] }} min{{ 'uut' if remaining_minutes == 1 else 'uten' }}
{%- endif %} over deze week.
{% endif %}
volume: 0.5
language: "NL"
- if:
- "{{ remaining < 0 }}"
then:
- service: input_boolean.turn_off
target:
entity_id: "{{ trigger.entity_id }}"
input_number.child_1_extra_screen_time:
icon: "mdi:gift"
input_number.child_1_correction_screen_time:
icon: "mdi:hammer"
input_number.child_2_extra_screen_time:
icon: "mdi:gift"
input_number.child_2_correction_screen_time:
icon: "mdi:hammer"
child_1_screen_time_active:
name: "Child 1 is consuming screen time"
child_2_screen_time_active:
name: "Child 2 is consuming screen time"
child_1_extra_screen_time:
name: "Child 1 extra screen time"
min: -600
max: 600
step: 1
mode: box
unit_of_measurement: "min"
child_1_correction_screen_time:
name: "Child 1 correction screen time"
min: -1500
max: 1500
step: 1
mode: box
unit_of_measurement: "min"
child_2_extra_screen_time:
name: "Child 2 extra screen time"
min: -600
max: 600
step: 1
mode: box
unit_of_measurement: "min"
child_2_correction_screen_time:
name: "Child 2 correction screen time"
min: -1500
max: 1500
step: 1
mode: box
unit_of_measurement: "min"
- platform: history_stats
name: Child 1 screen time this week
entity_id: input_boolean.child_1_screen_time_active
state: "on"
type: time
start: "{{ as_timestamp(now().replace(hour=0, minute=0, second=0)) - now().weekday() * 86400 }}"
end: "{{ now() }}"
- platform: history_stats
name: Child 1 screen time today
entity_id: input_boolean.child_1_screen_time_active
state: "on"
type: time
start: "{{ now().replace(hour=0, minute=0, second=0) }}"
end: "{{ now() }}"
- platform: history_stats
name: Child 2 screen time this week
entity_id: input_boolean.child_2_screen_time_active
state: "on"
type: time
start: "{{ as_timestamp(now().replace(hour=0, minute=0, second=0)) - now().weekday() * 86400 }}"
end: "{{ now() }}"
- platform: history_stats
name: Child 2 screen time today
entity_id: input_boolean.child_2_screen_time_active
state: "on"
type: time
start: "{{ now().replace(hour=0, minute=0, second=0) }}"
end: "{{ now() }}"
- unique_id: weekly_screen_time
name: "Weekly screen time"
state: "{{ 720 if is_state('binary_sensor.school_holiday', 'on') else 360 }}"
- unique_id: child_1_weekly_screen_time_threshold
name: "Child 1 weekly screen time threshold"
state: "{{ states('sensor.weekly_screen_time') | int(0) + states('input_number.child_1_extra_screen_time') | int(0) }}"
availability: "{{ states('sensor.weekly_screen_time') | is_number() and states('input_number.child_1_extra_screen_time') | is_number() }}"
unit_of_measurement: "min"
- unique_id: child_1_corrected_screen_time_this_week
name: "Child 1 corrected screen time this week"
state: "{{ states('sensor.child_1_screen_time_this_week') | float(0) * 60 - states('input_number.child_1_correction_screen_time') | int(0) }}"
availability: "{{ states('sensor.child_1_screen_time_this_week') | is_number() and states('input_number.child_1_correction_screen_time') | is_number() }}"
unit_of_measurement: "min"
- unique_id: child_1_weekly_screen_time_remaining
name: "Child 1 weekly screen time remaining"
state: "{{ states('sensor.child_1_weekly_screen_time_threshold') | int(0) - states('sensor.child_1_screen_time_this_week') | int(0) }}"
availability: "{{ states('sensor.child_1_weekly_screen_time_threshold') | is_number() and states('sensor.child_1_screen_time_this_week') | is_number() }}"
unit_of_measurement: "min"
- unique_id: child_2_weekly_screen_time_threshold
name: "Child 2 weekly screen time threshold"
state: "{{ states('sensor.weekly_screen_time') | int(0) + states('input_number.child_2_extra_screen_time') | int(0) }}"
availability: "{{ states('sensor.weekly_screen_time') | is_number() and states('input_number.child_2_extra_screen_time') | is_number() }}"
unit_of_measurement: "min"
- unique_id: child_2_corrected_screen_time_this_week
name: "Child 2 corrected screen time this week"
state: "{{ states('sensor.child_2_screen_time_this_week') | float(0) * 60 - states('input_number.child_2_correction_screen_time') | int(0) }}"
availability: "{{ states('sensor.child_2_screen_time_this_week') | is_number() and states('input_number.child_2_correction_screen_time') | is_number() }}"
unit_of_measurement: "min"
- unique_id: child_2_weekly_screen_time_remaining
name: "Child 2 weekly screen time remaining"
state: "{{ states('sensor.child_2_weekly_screen_time_threshold') | int(0) - states('sensor.child_2_screen_time_this_week') | int(0) }}"
availability: "{{ states('sensor.child_2_weekly_screen_time_threshold') | is_number() and states('sensor.child_2_screen_time_this_week') | is_number() }}"
unit_of_measurement: "min"
@bckthomas
Copy link

Hi
I love your works about screen time !
I have an error about the sensor.child_1_screen_time_this_week who is unavailable.
Where and how do you set it ?
Thanks

@khenderick
Copy link
Author

Thanks. I indeed forgot that part ๐Ÿ˜…. It seems my local setup evolved a little since I posted this gist, so I'm going to take a moment and see if I can refactor everything a bit, so it's easier to setup. I'll keep you posted.

@khenderick
Copy link
Author

I've updated the code. It seems the sensor.child_1_screen_time_this_week was already included; these are the history_stats sensors above. Since they don't have an explicit unique_id, it might be possible that the generated entity_id is different on your system.

@bckthomas
Copy link

bckthomas commented Dec 6, 2023

Sensor is always unavailable
I put all your code in one file, maybe you can take a look and see where is my mistake ?
history_stats sensor are below a "sensor:"
https://pastebin.com/5XnLtYNA

@bckthomas
Copy link

Waaait, it's OK ! I did a quick reboot of home assistant. After a full restart, I can see the sensor clearly !
Sorry for the inconvenience

@khenderick
Copy link
Author

Yeah, some entities cannot be reloaded, and need a restart. Glad it's working now ๐Ÿ˜Š.

@kleinbaum
Copy link

hi @khenderick - this screen time app looks great! i'm running into one issue: the weekly_screen_time_remaining sensor isn't working for me. It's showing the weekly time threshold, without subtracting the time used this week.

This is the sensor template code i'm using (with the XXX's replaced by my kid's name):

  • unique_id: xxxxx_weekly_screen_time_remaining
    name: "Xxxxx weekly screen time remaining"
    state: "{{ states('sensor.xxxxx_weekly_screen_time_threshold') | int(0) - states('sensor.xxxxx_screen_time_this_week') | int(0) }}"
    availability: "{{ states('sensor.xxxxx_weekly_screen_time_threshold') | is_number() and states('sensor.xxxxx_screen_time_this_week') | is_number() }}"
    unit_of_measurement: "min"

Seems like a pretty basic subtraction function - not sure why it's not working (but i'm new to home assistant and this coding format). Both the weekly_screen_time_threshold and the screen_time_this_week sensors shows the correct number - not sure why it's not just subtracting. Any idea what I'm doing wrong here? Thanks in advance!

@kleinbaum
Copy link

Never mind - i figured it out! It's treating the screen_time_this_week as Hours, but subtracting it from minutes - so an hour of screen time consumed recorded as just one minute in the weekly total. I added a * 60, and changed the int to float, and now it's working properly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment