Parental (wifi) Control Blueprint Help

Hello all, for about 2 years now I have been using HA to implement control of WiFi in my home, primarily to set the on and off time for WiFi for my kids, of which I have three. What I have is very basic but works well. I know when I was desperate for this, I looked around and didn’t find what I was looking for - we had Circle for a while, but for some reason got locked out, and got annoyed with it. Why am I writing here? I would love to see if it’s possible to create a blueprint for this such that others could benefit from it and perhaps enhance it with additional features we could all share. But, … honestly I haven’t spent any time whatsoever looking at how blueprints work to know how to configure one, or if what I’ve done is even compatible. Anyone want to work on this together over the holidays?

I have Ubiquiti (Unifi) WiFi setup and each device and a group per kid that I want to control are added as switched entities through the Unifi integration. A simple switch action can block and unblock devices, and I have a card for each kid with each of their devices and their group. It’s easy to manually switch on a particular device, for example, a chromebook for homework. That’s really all this does - it turns on and off WiFi for a particular set of devices at a particular time.

I set some variables - internet morning on time(same for all controlled devices), and then, for each kid, I have an internet off time, one for school days, and one for weekend days. There’s also a “today off time” for each kid, which is substituted depending if it’s a school day or not. Then I set a variable for each kit called “extra minutes”. Inevitably they will beg and/or do something nice to “earn” extra minutes. I put a slider on my lovelace dashboard to set an extra minutes time, which checks if it’s before or after the “zero hour”, if before, it adds that number of minutes to “today off time”, if after then it simply turns on, and then off after the amount of time set.

So far it’s been very reliable, except that Unifi sometimes won’t unblock properly, which I think is a long standing bug in Unifi software. At least it fails “safe” and I can manually unblock when needed.

The setup has variables, automations and lovelace dashboard components and plenty of initial manual setup - can all of that be packaged up into something reuseable? I wonder if it could be plugged to work with other router brands? Could it be integrated to pihole in some way to more robustly filter the enabled WiFi, who knows!? We have added devices since I built this that are not yet included, wouldn’t it be cool if, when a new device is detected the user could be asked to include or not include with a kid group?

Anyway let me know your thoughts and Happy Holidays!

1 Like

Hi @braddo

I have a similar problem with quite different solutions. We have several access points and also wired network, so I do the actual filtering at the router level using openwrt and shorewall firewall. The switches are controlled using nf_condition, that exposes the switches as individual files on the router system. Control of the switches is via ssh command currently, although its a little slow so I did wonder about using a network based interface instead.

I found that the scheduler card works with the switches, so I use that for the timed access. I like the idea of adding a certain amount of extra time - that would come in handy here too.

Lockdown was an issue, because we were constantly needing to allow access for school sites and balance that against the temptation of youtube and gaming sites. I know people have suggested elsewhere on the forum that we as parents should drill the necessary discipline into our children, but our experience has been that there is an age window where willpower is not enough for them and so we have had to add technical limitations too.

To solve the partial access problem, I have a squid proxy that allows access to a filtered list of sites. The list is adjusted depending on switches in the HA interface, using appdaemon to generate the squid configuration files when switches in HA change. That was not enough for some sites that refuse to go through the proxy, so I also have used dnsmasq’s ipset functionality to dynamically add IPs of some sites when particular DNS names are resolved.

I wondered on and off if it would be suitable to find an area within the forums were people could exchange ideas on this topic.

Hey Chris, thanks for the reply. Sorry seeing it a couple of weeks late. My post was in the Blueprints topic because I was looking for help creating a blueprint from which others might benefit. I know I searched and searched and did not find what I was looking for - quite surprised I didnt get much response to this post.

Sound like you have taken other approaches, great!

Again though I was looking for interest in building a Blueprint, maybe the interest just isn’t there.

Maybe you can get some inspiration from here?

Thanks for the link Tamsy. But I dont need help with blocking wifi - I built a great set of assets that has been working for a couple of years. A nicely featured little setup for managing kids internet time.

The question is regarding translating that to a blueprint that others could use and benefit from.

I thought folks familiar with blueprints might want to join forces and collaborate on transforming the existing functions as I havent researched how blueprints work.

Well, only because I started with this before blueprints were available.

I am interested! While my backend implementation is quite different to yours, I could adapt it to fit with the output from a setup based on blueprints.

My scripts don’t have the extra minutes feature, which would be helpful for us. Currently we have to remember to manually switch off, and the darling offspring have noticed that we can be quite forgetful on that point and come up with all sorts of reasons to use the internet briefly in the hope that it will be forgotten!

Maybe we could have a look at moving that into a blueprint that I could make use of too?

I don’t have kids and am just browsing through the community posts…
That being said, I wanna throw in my 2 cents if I may.

I find challs solution a slight hint better, since he’s not blocking the children’s devices from the network at all - just cutting it from actual internet usage/content consumption. I mean, blocking children’s devices from the wifi disables them from locally accessing Home Assistant at all, isn’t it?

As for a blueprint implementation I would suggest to break it down into two different parts:

  • Parental Control “Mgmt” Blueprint
    This would contain all the handling for “extra time”, “home-schooling”, “allowed sites” etc.
  • Parental Control “Impl” Blueprint
    This would contain all the handling to actually implement the rules within the network.

This separation would make it more flexible so different homes can implement it in different ways (like I’ve seen here).

But, since it’s just you two talking about it and the issue being stale for a while now… I’m wondering if there’s a broader demand for that :grin:

Actually, there’s a problem with allowing my children access to HA via mobile app because its difficult to prevent the app from turning any switch on or off… including the parental control settings. I’ve noticed that it should be possible to use the context to get the user that triggered a change, so I could use an automation to try and reverse the change. But that probably won’t be really easy to get it working properly. I have noticed some quiet effort in the HA codebase to add some sort of security restriction settings, so I’m hoping there might be an announcement about that coming in the near future.

Yes, once you start trying to think about all the possible uses it does get quite complicated.

You can restrict dashboards for just admins. I don’t how tech savy your kids are but that might restrict them enough?

Hi, unfortunately that wasn’t enough to prevent the mobile app from being able to access those switches - see my post

Having said that, I did actually get round to making an automation to do this. It was sufficient to discourage my kids from playing with the settings again after that.

I’d forgotten that I’d posted about it here - thanks for the reminder. I’ll add a post with the scripts in case anyone finds them useful.

Here are the scripts I used to undo changes of key settings by users of the home assistant app.

There are two separate scripts. This is because I needed separate behaviour: switches and inputs need to be turned back off if they are turned on. And scripts, automations and schedules need to be turned back on if they are turned off. I didn’t bother to factor them together, so they are 2 separate scripts with ‘off’ and ‘on’ basically reversed.

I wasn’t able to completely prevent the switch, so the approach I took was to monitor for changes in state, and then immediately revert the change. Several times… to counteract attempts to brute force the system. Gotta give them credit for being persistent at trying to break it … :slight_smile:

I also send a message if attempts are made to change the settings.

If you want to use these, replace the ‘parent’ and ‘childX’ with the actual user names.

Switch monitor

- id: '1670954893000'
  alias: Switch sentry - disallow turning on by certain users
  description: ''
  trigger:
  - platform: state
    entity_id:
    - input_boolean.boolean1
    - input_boolean.boolean2
    - input_select.select1
    - switch.switch1
    to: 'on'
  action:
  - variables:
      user_id: '{{ trigger.to_state.context.user_id }}'
      entity: '{{ trigger.entity_id }}'
      from_state: '{{ trigger.from_state.state }}'
      to_state: '{{ trigger.to_state.state }}'
      changed_by_user: "{% if trigger.to_state.context.user_id is defined %}\n  {{
        states.person|selectattr(\n      \"attributes.user_id\", \"==\", trigger.to_state.context.user_id\n
        \   )|map(attribute=\"attributes.friendly_name\")|first\n  }}\n{% endif %}
        \     \n"
  - alias: Stop if no user caused the trigger
    condition:
    - ' {{ user_id != None }} '
  - alias: Get user who changed
    variables:
      changed_by_user: "{{ states.person|selectattr(\n    \"attributes.user_id\",
        \"==\", trigger.to_state.context.user_id\n  )|map(attribute=\"attributes.friendly_name\")|first\n}}\n"
  - alias: Determine if change was allowed
    variables:
      forbidden: '{{ changed_by_user in disallowed_users }}

        '
  - service: system_log.write
    data:
      message: 'User id who triggered: {{ user_id }}

        User who triggered: {{ changed_by_user }}

        Forbidden: {{ forbidden }}

        '
      level: error
  - if:
    - ' {{ forbidden }} '
    then:
    - service: system_log.write
      data:
        level: info
        message: 'User who triggered: {{ trigger.to_state.context.user_id }}

          Disallowed? {{  (trigger.to_state.context.user_id != None and states.person|selectattr("attributes.user_id",
          "==", trigger.to_state.context.user_id)|map(attribute="attributes.friendly_name")|first
          in disallowed_users) }}

          '
    - service: script.notify_people
      data:
        person: parent
        title: Forbidden change made by {{ changed_by_user }}
        message: '{{ entity }} {{ from_state }} -> {{ to_state }}

          '
    - service: homeassistant.turn_off
      target:
        entity_id: '{{ entity }}'
    - wait_template: '{{ is_state(entity, ''on'') }}'
      alias: Wait just in case the switch turn on was triggered
      timeout: 00:00:30
      continue_on_timeout: false
    - service: homeassistant.turn_off
      target:
        entity_id: '{{ entity }}'
    - wait_template: '{{ is_state(entity, ''on'') }}'
      alias: Wait just in case the switch turn on was triggered
      timeout: 00:00:30
      continue_on_timeout: false
    - service: homeassistant.turn_off
      target:
        entity_id: '{{ entity }}'
  variables:
    disallowed_users:
    - child1
    - child2
  mode: parallel
  max: 20

Second script for automations, scripts and schedules, where I replaced off with on:

- id: '1670954893001'
  alias: Script monitor - disallow turning off
  description: ''
  trigger:
  - platform: state
    entity_id:
    - script.script1
    - automation.automation1
    - automation.automation2
    - switch.schedule_d2cdb8
    to: 'off'
  condition: []
  action:
  - variables:
      user_id: '{{ trigger.to_state.context.user_id }}'
      entity: '{{ trigger.entity_id }}'
      from_state: '{{ trigger.from_state.state }}'
      to_state: '{{ trigger.to_state.state }}'
      changed_by_user: "{% if trigger.to_state.context.user_id is defined %}\n  {{
        states.person|selectattr(\n      \"attributes.user_id\", \"==\", trigger.to_state.context.user_id\n
        \   )|map(attribute=\"attributes.friendly_name\")|first\n  }}\n{% endif %}
        \     \n"
  - alias: Stop if no user caused the trigger
    condition:
    - ' {{ user_id != None }} '
  - alias: Get user who changed
    variables:
      changed_by_user: "{{ states.person|selectattr(\n    \"attributes.user_id\",
        \"==\", trigger.to_state.context.user_id\n  )|map(attribute=\"attributes.friendly_name\")|first\n}}\n"
  - alias: Determine if change was allowed
    variables:
      forbidden: '{{ changed_by_user in disallowed_users }}

        '
  - service: system_log.write
    data:
      message: 'User id who triggered: {{ user_id }}

        User who triggered: {{ changed_by_user }}

        Forbidden: {{ forbidden }}

        '
      level: error
  - if:
    - ' {{ forbidden }} '
    then:
    - service: system_log.write
      data:
        level: info
        message: 'User who triggered: {{ trigger.to_state.context.user_id }}

          Disallowed? {{  (trigger.to_state.context.user_id != None and states.person|selectattr("attributes.user_id",
          "==", trigger.to_state.context.user_id)|map(attribute="attributes.friendly_name")|first
          in disallowed_users) }}

          '
    - service: script.notify_people
      data:
        person: parent
        title: Forbidden change made by {{ changed_by_user }}
        message: '{{ entity }} {{ from_state }} -> {{ to_state }}

          '
    - service: homeassistant.turn_on
      target:
        entity_id: '{{ entity }}'
    - wait_template: '{{ is_state(entity, ''off'') }}'
      alias: Wait just in case the switch turn off was triggered
      timeout: 00:00:30
      continue_on_timeout: false
    - service: homeassistant.turn_on
      target:
        entity_id: '{{ entity }}'
    - wait_template: '{{ is_state(entity, ''off'') }}'
      alias: Wait just in case the switch turn off was triggered
      timeout: 00:00:30
      continue_on_timeout: false
    - service: homeassistant.turn_on
      target:
        entity_id: '{{ entity }}'
  mode: parallel
  variables:
    disallowed_users:
    - child1
    - child2
  max: 20

The notify_people script is a script I use to send notifications to phones etc., but not if the system is set to quiet mode. It looks like this:

alias: Notify people
mode: parallel
icon: mdi:message-alert
max: 30
description: Send notification to the given people or group
fields:
  person:
    description: Who should receive the notification
    selector:
      select:
        options:
          - parent1
          - parent2
          - parents
          - bedrooms
          - tv
          - all
  title:
    description: The title of the notification
    example: State change
    selector:
      text: null
  message:
    description: The message content
    example: The light is on!
    selector:
      text:
        multiline: true
sequence:
  - condition: state
    entity_id: input_boolean.quiet_mode
    state: "off"
  - if:
      - "{{ person in ['all'] }}"
    then:
      - service: notify.mobile_app_parent1
        data:
          title: "{{ title }}"
          message: "{{ message }}"
      - service: notify.mobile_app_parent2
        data:
          title: "{{ title }}"
          message: "{{ message }}"
  - if:
      - "{{ person in ['all', 'tv'] }}"
    then:
      - service: notify.tv1
        data:
          title: "{{ title }}"
          message: "{{ message }}"