Added wifischedule
Turns WiFi on and off according to a schedule Signed-off-by: Nils Koenig <openwrt@newk.it>
This commit is contained in:
parent
4b5577016c
commit
ad34a2b53c
5 changed files with 488 additions and 0 deletions
11
net/wifischedule/LICENSE
Normal file
11
net/wifischedule/LICENSE
Normal file
|
@ -0,0 +1,11 @@
|
|||
Copyright (c) 2016, prpl Foundation
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without
|
||||
fee is hereby granted, provided that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
|
||||
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
51
net/wifischedule/Makefile
Normal file
51
net/wifischedule/Makefile
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Copyright (c) 2016, prpl Foundation
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without
|
||||
# fee is hereby granted, provided that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
|
||||
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
# FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
# Author: Nils Koenig <openwrt@newk.it>
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wifischedule
|
||||
PKG_VERSION:=1
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=PRPL
|
||||
|
||||
PKG_MAINTAINER:=Nils Koenig <openwrt@newk.it>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/wifischedule
|
||||
SUBMENU:=wireless
|
||||
TITLE:=Turns WiFi on and off according to a schedule
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
endef
|
||||
|
||||
define Package/wifischedule/description
|
||||
Turns WiFi on and off according to a schedule defined in UCI.
|
||||
endef
|
||||
|
||||
define Package/wifischedule/conffiles
|
||||
/etc/config/wifi_schedule
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/wifischedule/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) ./net/usr/bin/wifi_schedule.sh $(1)/usr/bin/wifi_schedule.sh
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_DATA) ./net/etc/config/wifi_schedule $(1)/etc/config/wifi_schedule
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,wifischedule))
|
86
net/wifischedule/README.md
Normal file
86
net/wifischedule/README.md
Normal file
|
@ -0,0 +1,86 @@
|
|||
# wifischedule
|
||||
Turns WiFi on and off according to a schedule on an openwrt router
|
||||
|
||||
## Components
|
||||
* wifischedule: Shell script that creates cron jobs based on configuration provided in UCI and does all the other logic of enabling and disabling wifi with the use of `/sbin/wifi` and `/usr/bin/iwinfo`. Can be used standalone.
|
||||
* luci-app-wifischedule: LUCI frontend for creating the UCI configuration and triggering the actions. Depends on wifischedule.
|
||||
|
||||
|
||||
## Use cases
|
||||
You can create user-defined events when to enable or disable WiFi.
|
||||
There are various use cases why you would like to do so:
|
||||
|
||||
1. Reduce power consumption and therefore reduce CO2 emissions.
|
||||
2. Reduce emitted electromagnatic radiation.
|
||||
3. Force busincess hours when WiFi is available.
|
||||
|
||||
Regarding 1: Please note, that you need to unload the wireless driver modules in order to get the most effect of saving power.
|
||||
In my test scenario only disabling WiFi saves about ~0.4 Watt, unloading the modules removes another ~0.4 Watt.
|
||||
|
||||
Regarding 2: Think of a wireless accesspoint e.g. in your bedrom, kids room where you want to remove the ammount of radiation emitted.
|
||||
|
||||
Regarding 3: E.g. in a company, why would wireless need to be enabled weekends if no one is there working?
|
||||
Or think of an accesspoint in your kids room when you want the youngsters to sleep after 10 pm instead of facebooking...
|
||||
|
||||
## Configuration
|
||||
You can create an arbitrary number of schedule events. Please note that there is on sanity check done wheather the start / stop times overlap or make sense.
|
||||
If start and stop time are equal, this leads to disabling the WiFi at the given time.
|
||||
|
||||
Logging if enabled is done to the file `/var/log/wifi_schedule.log` and can be reviewed through the "View Logfile" tab.
|
||||
The cron jobs created can be reviewed through the "View Cron Jobs" tab.
|
||||
|
||||
Please note that the "Unload Modules" function is currently considered as experimental. You can manually add / remove modules in the text field.
|
||||
The button "Determine Modules Automatically" tries to make a best guess determining regarding the driver module and its dependencies.
|
||||
When un-/loading the modules, there is a certain number of retries (`module_load`) performed.
|
||||
|
||||
The option "Force disabling wifi even if stations associated" does what it says - when activated it simply shuts down WiFi.
|
||||
When unchecked, its checked every `recheck_interval` minutes if there are still stations associated. Once the stations disconnect, WiFi is disabled.
|
||||
|
||||
Please note, that the parameters `module_load` and `recheck_interval` are only accessible through uci.
|
||||
|
||||
## UCI Configuration `wifi_schedule`
|
||||
UCI configuration file: `/etc/config/wifi_schedule`:
|
||||
|
||||
```
|
||||
config global
|
||||
option logging '0'
|
||||
option enabled '0'
|
||||
option recheck_interval '10'
|
||||
option modules_retries '10'
|
||||
|
||||
config entry 'Businesshours'
|
||||
option enabled '0'
|
||||
option daysofweek 'Monday Tuesday Wednesday Thursday Friday'
|
||||
option starttime '06:00'
|
||||
option stoptime '22:00'
|
||||
option forcewifidown '0'
|
||||
|
||||
config entry 'Weekend'
|
||||
option enabled '0'
|
||||
option daysofweek 'Saturday Sunday'
|
||||
option starttime '00:00'
|
||||
option stoptime '00:00'
|
||||
option forcewifidown '1'
|
||||
```
|
||||
|
||||
## Script: `wifi_schedule.sh`
|
||||
This is the script that does the work. Make your changes to the UCI config file: `/etc/config/wifi_schedule`
|
||||
|
||||
Then call the script as follows in order to get the necessary cron jobs created:
|
||||
|
||||
`wifi_schedule.sh cron`
|
||||
|
||||
All commands:
|
||||
|
||||
```
|
||||
wifi_schedule.sh cron|start|stop|forcestop|recheck|getmodules|savemodules|help
|
||||
|
||||
cron: Create cronjob entries.
|
||||
start: Start wifi.
|
||||
stop: Stop wifi gracefully, i.e. check if there are stations associated and if so keep retrying.
|
||||
forcestop: Stop wifi immediately.
|
||||
recheck: Recheck if wifi can be disabled now.
|
||||
getmodules: Returns a list of modules used by the wireless driver(s)
|
||||
savemodules: Saves a list of automatic determined modules to UCI
|
||||
help: This description.
|
||||
```
|
19
net/wifischedule/net/etc/config/wifi_schedule
Normal file
19
net/wifischedule/net/etc/config/wifi_schedule
Normal file
|
@ -0,0 +1,19 @@
|
|||
config global
|
||||
option logging '0'
|
||||
option enabled '0'
|
||||
option recheck_interval '10'
|
||||
option modules_retries '10'
|
||||
|
||||
config entry 'Businesshours'
|
||||
option enabled '0'
|
||||
option daysofweek 'Monday Tuesday Wednesday Thursday Friday'
|
||||
option starttime '06:00'
|
||||
option stoptime '22:00'
|
||||
option forcewifidown '0'
|
||||
|
||||
config entry 'Weekend'
|
||||
option enabled '0'
|
||||
option daysofweek 'Saturday Sunday'
|
||||
option starttime '00:00'
|
||||
option stoptime '00:00'
|
||||
option forcewifidown '1'
|
321
net/wifischedule/net/usr/bin/wifi_schedule.sh
Executable file
321
net/wifischedule/net/usr/bin/wifi_schedule.sh
Executable file
|
@ -0,0 +1,321 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2016, prpl Foundation
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without
|
||||
# fee is hereby granted, provided that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
|
||||
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
# FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
# Author: Nils Koenig <openwrt@newk.it>
|
||||
|
||||
SCRIPT=$0
|
||||
LOCKFILE=/tmp/wifi_schedule.lock
|
||||
LOGFILE=/tmp/log/wifi_schedule.log
|
||||
LOGGING=0 #default is off
|
||||
PACKAGE=wifi_schedule
|
||||
GLOBAL=${PACKAGE}.@global[0]
|
||||
|
||||
_log()
|
||||
{
|
||||
if [ ${LOGGING} -eq 1 ]; then
|
||||
local ts=$(date)
|
||||
echo "$ts $@" >> ${LOGFILE}
|
||||
fi
|
||||
}
|
||||
|
||||
_exit()
|
||||
{
|
||||
local rc=$1
|
||||
lock -u ${LOCKFILE}
|
||||
exit ${rc}
|
||||
}
|
||||
|
||||
_cron_restart()
|
||||
{
|
||||
/etc/init.d/cron restart > /dev/null
|
||||
}
|
||||
|
||||
_add_cron_script()
|
||||
{
|
||||
(crontab -l ; echo "$1") | sort | uniq | crontab -
|
||||
_cron_restart
|
||||
}
|
||||
|
||||
_rm_cron_script()
|
||||
{
|
||||
crontab -l | grep -v "$1" | sort | uniq | crontab -
|
||||
_cron_restart
|
||||
}
|
||||
|
||||
_get_uci_value_raw()
|
||||
{
|
||||
local value
|
||||
value=$(uci get $1 2> /dev/null)
|
||||
local rc=$?
|
||||
echo ${value}
|
||||
return ${rc}
|
||||
}
|
||||
|
||||
_get_uci_value()
|
||||
{
|
||||
local value
|
||||
value=$(_get_uci_value_raw $1)
|
||||
local rc=$?
|
||||
if [ ${rc} -ne 0 ]; then
|
||||
_log "Could not determine UCI value $1"
|
||||
return 1
|
||||
fi
|
||||
echo ${value}
|
||||
}
|
||||
|
||||
_format_dow_list()
|
||||
{
|
||||
local dow=$1
|
||||
local flist=""
|
||||
local day
|
||||
for day in ${dow}
|
||||
do
|
||||
if [ ! -z ${flist} ]; then
|
||||
flist="${flist},"
|
||||
fi
|
||||
flist="${flist}${day:0:3}"
|
||||
done
|
||||
echo ${flist}
|
||||
}
|
||||
|
||||
|
||||
_enable_wifi_schedule()
|
||||
{
|
||||
local entry=$1
|
||||
local starttime
|
||||
local stoptime
|
||||
starttime=$(_get_uci_value ${PACKAGE}.${entry}.starttime) || _exit 1
|
||||
stoptime=$(_get_uci_value ${PACKAGE}.${entry}.stoptime) || _exit 1
|
||||
|
||||
local dow
|
||||
dow=$(_get_uci_value_raw ${PACKAGE}.${entry}.daysofweek) || _exit 1
|
||||
|
||||
local fdow=$(_format_dow_list "$dow")
|
||||
local forcewifidown
|
||||
forcewifidown=$(_get_uci_value ${PACKAGE}.${entry}.forcewifidown)
|
||||
local stopmode="stop"
|
||||
if [ $forcewifidown -eq 1 ]; then
|
||||
stopmode="forcestop"
|
||||
fi
|
||||
|
||||
|
||||
local stop_cron_entry="$(echo ${stoptime} | awk -F':' '{print $2, $1}') * * ${fdow} ${SCRIPT} ${stopmode}" # ${entry}"
|
||||
_add_cron_script "${stop_cron_entry}"
|
||||
|
||||
if [[ $starttime != $stoptime ]]
|
||||
then
|
||||
local start_cron_entry="$(echo ${starttime} | awk -F':' '{print $2, $1}') * * ${fdow} ${SCRIPT} start" # ${entry}"
|
||||
_add_cron_script "${start_cron_entry}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_get_wireless_interfaces()
|
||||
{
|
||||
local n=$(cat /proc/net/wireless | wc -l)
|
||||
cat /proc/net/wireless | tail -n $(($n - 2))|awk -F':' '{print $1}'| sed 's/ //'
|
||||
}
|
||||
|
||||
|
||||
get_module_list()
|
||||
{
|
||||
local mod_list
|
||||
local _if
|
||||
for _if in $(_get_wireless_interfaces)
|
||||
do
|
||||
local mod=$(basename $(readlink -f /sys/class/net/${_if}/device/driver))
|
||||
local mod_dep=$(modinfo ${mod} | awk '{if ($1 ~ /depends/) print $2}')
|
||||
mod_list=$(echo -e "${mod_list}\n${mod},${mod_dep}" | sort | uniq)
|
||||
done
|
||||
echo $mod_list | tr ',' ' '
|
||||
}
|
||||
|
||||
save_module_list_uci()
|
||||
{
|
||||
local list=$(get_module_list)
|
||||
uci set ${GLOBAL}.modules="${list}"
|
||||
uci commit ${PACKAGE}
|
||||
}
|
||||
|
||||
_unload_modules()
|
||||
{
|
||||
local list=$(_get_uci_value ${GLOBAL}.modules)
|
||||
local retries
|
||||
retries=$(_get_uci_value ${GLOBAL}.modules_retries) || _exit 1
|
||||
_log "unload_modules ${list} (retries: ${retries})"
|
||||
local i=0
|
||||
while [[ ${i} -lt ${retries} && "${list}" != "" ]]
|
||||
do
|
||||
i=$(($i+1))
|
||||
local mod
|
||||
local first=0
|
||||
for mod in ${list}
|
||||
do
|
||||
if [ $first -eq 0 ]; then
|
||||
list=""
|
||||
first=1
|
||||
fi
|
||||
rmmod ${mod} > /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
list="$list $mod"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
_load_modules()
|
||||
{
|
||||
local list=$(_get_uci_value ${GLOBAL}.modules)
|
||||
local retries
|
||||
retries=$(_get_uci_value ${GLOBAL}.modules_retries) || _exit 1
|
||||
_log "load_modules ${list} (retries: ${retries})"
|
||||
local i=0
|
||||
while [[ ${i} -lt ${retries} && "${list}" != "" ]]
|
||||
do
|
||||
i=$(($i+1))
|
||||
local mod
|
||||
local first=0
|
||||
for mod in ${list}
|
||||
do
|
||||
if [ $first -eq 0 ]; then
|
||||
list=""
|
||||
first=1
|
||||
fi
|
||||
modprobe ${mod} > /dev/null 2>&1
|
||||
rc=$?
|
||||
if [ $rc -ne 255 ]; then
|
||||
list="$list $mod"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
_create_cron_entries()
|
||||
{
|
||||
local entries=$(uci show ${PACKAGE} 2> /dev/null | awk -F'.' '{print $2}' | grep -v '=' | grep -v '@global\[0\]' | uniq | sort)
|
||||
local _entry
|
||||
for entry in ${entries}
|
||||
do
|
||||
local status
|
||||
status=$(_get_uci_value ${PACKAGE}.${entry}.enabled) || _exit 1
|
||||
if [ ${status} -eq 1 ]
|
||||
then
|
||||
_enable_wifi_schedule ${entry}
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_cron_status()
|
||||
{
|
||||
local global_enabled
|
||||
global_enabled=$(_get_uci_value ${GLOBAL}.enabled) || _exit 1
|
||||
_rm_cron_script "${SCRIPT}"
|
||||
if [ ${global_enabled} -eq 1 ]; then
|
||||
_create_cron_entries
|
||||
fi
|
||||
}
|
||||
|
||||
disable_wifi()
|
||||
{
|
||||
_rm_cron_script "${SCRIPT} recheck"
|
||||
/sbin/wifi down
|
||||
local unload_modules
|
||||
unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
|
||||
if [[ "${unload_modules}" == "1" ]]; then
|
||||
_unload_modules
|
||||
fi
|
||||
}
|
||||
|
||||
soft_disable_wifi()
|
||||
{
|
||||
local _disable_wifi=1
|
||||
local iwinfo=/usr/bin/iwinfo
|
||||
if [ ! -e ${iwinfo} ]; then
|
||||
_log "${iwinfo} not available, skipping"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check if no stations are associated
|
||||
local _if
|
||||
for _if in $(_get_wireless_interfaces)
|
||||
do
|
||||
output=$(${iwinfo} ${_if} assoclist)
|
||||
if [[ "$output" != "No station connected" ]]
|
||||
then
|
||||
_disable_wifi=0
|
||||
local stations=$(echo ${output}| grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}' | tr '\n' ' ')
|
||||
_log "Station(s) ${stations}associated on ${_if}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${_disable_wifi} -eq 1 ]; then
|
||||
_log "No stations associated, disable wifi."
|
||||
disable_wifi
|
||||
else
|
||||
_log "Could not disable wifi due to associated stations, retrying..."
|
||||
local recheck_interval=$(_get_uci_value ${GLOBAL}.recheck_interval)
|
||||
_add_cron_script "*/${recheck_interval} * * * * ${SCRIPT} recheck"
|
||||
fi
|
||||
}
|
||||
|
||||
enable_wifi()
|
||||
{
|
||||
_rm_cron_script "${SCRIPT} recheck"
|
||||
local unload_modules
|
||||
unload_modules=$(_get_uci_value_raw ${GLOBAL}.unload_modules) || _exit 1
|
||||
if [[ "${unload_modules}" == "1" ]]; then
|
||||
_load_modules
|
||||
fi
|
||||
/sbin/wifi
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
echo ""
|
||||
echo "$0 cron|start|stop|forcestop|recheck|getmodules|savemodules|help"
|
||||
echo ""
|
||||
echo " UCI Config File: /etc/config/${PACKAGE}"
|
||||
echo ""
|
||||
echo " cron: Create cronjob entries."
|
||||
echo " start: Start wifi."
|
||||
echo " stop: Stop wifi gracefully, i.e. check if there are stations associated and if so keep retrying."
|
||||
echo " forcestop: Stop wifi immediately."
|
||||
echo " recheck: Recheck if wifi can be disabled now."
|
||||
echo " getmodules: Returns a list of modules used by the wireless driver(s)"
|
||||
echo " savemodules: Saves a list of automatic determined modules to UCI"
|
||||
echo " help: This description."
|
||||
echo ""
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# MAIN
|
||||
###############################################################################
|
||||
LOGGING=$(_get_uci_value ${GLOBAL}.logging) || _exit 1
|
||||
_log ${SCRIPT} $1 $2
|
||||
lock ${LOCKFILE}
|
||||
|
||||
case "$1" in
|
||||
cron) check_cron_status ;;
|
||||
start) enable_wifi ;;
|
||||
forcestop) disable_wifi ;;
|
||||
stop) soft_disable_wifi ;;
|
||||
recheck) soft_disable_wifi ;;
|
||||
getmodules) get_module_list ;;
|
||||
savemodules) save_module_list_uci ;;
|
||||
help|--help|-h|*) usage ;;
|
||||
esac
|
||||
|
||||
_exit 0
|
Loading…
Reference in a new issue