Cut Internet temporarily in a device in your LAN (or Applying for the Worst Daddy Award 2017)
Background
(take it with a grain of salt)
Internet is great. Children are great, too. The combination of both can be sometimes problematic. Tablets are very attractive devices for children, which maybe are not mature enough to make a proper use. Well, some adults are not good examples either.
If you want to limit the Internet use of these little criatures that are wandering around your house but you are not brave enough to tell them to give their tablets to you, keep on reading. At the end of the day they’ll keep their digital devices with them but without Internet. They will blame your Internet Service Provider and then start playing with other toys or children or… you.
Objective
The main aim is to setup an automation to disconnect temporarily a device in our LAN using, obviously, Home Assistant on a Raspberry Pi with Hassbian (it could also work in other scenarios but I’m not 100% sure).
(My usual) Disclaimer: I’m a computer hobbyist, self-learner, and with proficiency in copy-paste tool. What I describe here has worked for me but YMMV). As you may have guessed, my mother language is not English so sorry for any vocabulary inaccuracy, and so on.
(Additional) Disclaimer: In this post I assume that you are fairly comfortable with the command line usage, know how to ssh to your RPi and are used to concepts such as IP, MAC address, sudo, and stuff like that. Typical things if you belong to the HA community.
Steps
Step 1.- Getting the IP address
It’s pretty obvious that we must know something from the device where we want to cut the Internet connection. If you have set a static IP for this device in your LAN you can skip this step. I’m assuming now that you know the MAC address and not the IP. For this we will use nmap, which should be already available in Hassbian distribution. So, ssh to your RPi and write:
pi@hassbian:~ $ nmap -sP ROUTER_ADRS/24 >/dev/null && arp -an | grep CHILD_MAC | awk '{print $2}' | sed 's/[()]//g'
where ROUTER_ADRS is your router IP (for example: 192.168.0.1) and CHILD_MAC is the MAC address of the device (for example: AA:BB:CC:DD:EE:FF). The previous command will show the device’s IP address in the screen.
Step 2.- Installing arpspoof
We will use the arpspoof command later. We have to install it first. I’ll save you a Google search; it belongs to the dsniff package, so:
pi@hassbian:~ $ sudo apt-get install dsniff
Step 3.- Using arpspoof
You need to run arpspoof with the sudo command, so:
pi@hassbian:~ $ sudo arpspoof -i INTERFACE -t CHILD_ADRS ROUTER_ADRS
where INTERFACE is the network interface used by the RPi to connect to the router (typically eth0 if it is wire-connected, wlan0 if it is wifi) and CHILD_ADRS is the device’s IP address obtained in Step 1.
Once you press Enter you’ll see some messages in the screen and you won’t be able to connect to the Internet with the device (try it!) until you stop arpspoofing by pressing Ctrl+C. I’m not going to spend any time commenting what is happening behind the scenes. Wikipedia and Google are your friends.
Since arpspoof (i) needs root privileges, (ii) will be used later in a bash script, and (iii) we don’t want to be asked for the password, we need to make some modifications to the system to allow the homeassistant user run arpspoof. This is accomplished by adding these two lines to the /etc/sudoers.d/020_homeassistant_hassbian-scripts file:
homeassistant ALL=(ALL) NOPASSWD: /usr/sbin/arpspoof
homeassistant ALL=(ALL) NOPASSWD: /usr/bin/killall arpspoof
Disclaimer: I don’t know 100% the consequences in terms of security issues when doing this. You’ve been warned.
Step 4.- Setting up everything in HA
Ok, so what we are trying to accomplish is this card in the frontend:
which is grouped and saved, in my case, in .homeassistant/includes/groups/internet_control.yaml :
internet:
name: 'Internet'
control: hidden
entities:
- automation.disconnect_ipad2
- input_slider.internet_cut_time
- input_slider.internet_cut_hour
- input_slider.internet_cut_minutes
- sensor.internetcuttime
It consists of three input_sliders. The first one (internet_cut_time) defines the duration in minutes that is going to take the Internet disconnection. The second and third sliders define the hour of the day when the disconnection is going to hapen: (internet_cut_hour) defines the hour and (internet_cut_minutes) defines the minutes. In my setup all this is defined in this file .homeassistant/includes/input_sliders/internet_cuttime.yaml :
internet_cut_time:
name: 'Disconnection time (min)'
initial: 120
min: 2
max: 240
step: 1
internet_cut_hour:
name: Hour
icon: mdi:timer
initial: 1
min: 0
max: 23
step: 1
internet_cut_minutes:
name: Minutes
icon: mdi:timer
initial: 05
min: 0
max: 55
step: 5
There is also a sensor created to easily visualize the moment when the disconnection will happen. It gathers the information from the two previous sliders. In my setup this sensor is defined in the file .homeassistant/includes/sensors/internet_cut.yaml :
- platform: template
sensors:
internetcuttime:
friendly_name: Switch off at
entity_id:
- input_slider.internet_cut_hour
- input_slider.internet_cut_minutes
value_template: '{{ "%0.02d:%0.02d" | format(states("input_slider.internet_cut_hour") | int, states("input_slider.internet_cut_minutes") | int) }}'
The first element of the card is the automation. In my case, I’ve migrated from the split automation configuration to the one-file automation configuration. So, I have this entry in my automations.yaml file:
- id: id031
alias: 'Disconnect iPad2'
trigger:
platform: time
minutes: '/5'
seconds: '0'
condition:
condition: template
value_template: '{{ ((now().strftime("%s") | int ) | timestamp_custom("%H:%M")) == states.sensor.internetcuttime.state }}'
action:
- service: shell_command.cut_internet_temp
As you can see, the automation calls a shell_command that I have placed in the file .homeassistant/includes/shell_commands/cut_internet.yaml :
cut_internet_temp: '/home/homeassistant/.homeassistant/includes/shell_scripts/cut_internet.sh {{ states.input_slider.internet_cut_time.state }}'
which calls a bash script with the Internet disconnection duration as argument. I have the bash scripts stored in .homeassistant/includes/shell_scripts folder and the file cut_internet.sh gathers the instructions we already saw in Steps 1 and 3. This is the file contents (you have to update the router’s IP, the MAC address of the device you want to disconnect and the network interface the RPi uses):
#!/bin/bash
# The argument is saved in CUT_TIME variable
CUT_TIME=$1
# Update your router's IP
ROUTER_ADRS=WWW.XXX.YYY.ZZZ
# Update the device's MAC address
CHILD_MAC=AA:BB:CC:DD:EE:FF
# Update the network interface
INTERFACE=eth0
# Finding the device's IP address based on the MAC
CHILD_ADRS=$(nmap -sP ${ROUTER_ADRS}/24 >/dev/null && arp -an | grep ${CHILD_MAC} | awk '{print $2}' | sed 's/[()]//g')
# Arpspoofing the device
nohup sudo arpspoof -i ${INTERFACE} -t ${CHILD_ADRS} ${ROUTER_ADRS} > /dev/null 2>&1 &
# Pausing the script for CUT_TIME minutes (arpspoof is active during this time)
sleep ${CUT_TIME}m
# Killing the arpspoof command (the disconnection ends)
sudo killall arpspoof
# I have to check what happens if there are more than one instance of this script running in parallel
# and the first one that ends kills the arpspoof commands of the rest (due to the killall)
Finally, all the related components appear in my configuration.yaml file as:
group: !include_dir_merge_named includes/groups
input_slider: !include_dir_merge_named includes/input_sliders
sensor: !include_dir_merge_list includes/sensors
shell_command: !include_dir_merge_named includes/shell_commands
automation: !include automations.yaml
Final words
If I haven’t been clear enough, please ask. I’ll try to do my best to help.
It is very likely that this can be accomplished in a simpler/safer way (remember the disclaimers). I’m ready to follow your hints (if any) and keep on learning.
The current automation only cuts Internet based on the hour of the day. My next step is to include an automation taking into account how much time the device has been connected during the day. I’ll continue to update this post as I make progress. You are invited to share your improvements and automations too.
Thanks for reading!