My server runs a variety of services including XBMC, squeezecenter and a squeezeslave client. I also use it as a file server with my main access being through ssh. I wanted a way to shutdown the server down after being idle for a set time period but couldn't see any easy way of doing this as these services don't talk to each other.
After researching many forums I decided to just write my own python script and run it as an upstart service. I'm not a programmer so the code is not pretty, however it does work. I thought it might be useful to others so have posted it here. If anyone has any improvements or suggestions please post below.
How it works:
I set my xbmc to display a screensaver after 2 minutes of inactivity. The screen saver is a slideshow of fanart in /home/username/Fanart. The script checks if xbmc is playing anything from this folder and hence detects if the screensaver is running. The reason for not using the proper xbmc fanart folder is to avoid false positives during normal use of the menus, though the risk of this happening is small.
The script also connects to squeezecenter and checks each player in turn to see if they are paused or stopped. If all players are inactive squeezecenter is considered idle. Note that squeezeboxes being powered on doesn't constitute activity in the below code, however this can easily be changed.
Additionally, if the file /home/username/nosleep exists the script won't shutdown the computer.
The script then checks to see if any ssh connections are currently open. If there are any established connections then shutdown will be prevented.
The script will do the above checks every 30 seconds. Once all systems have been idle for 20 minutes the server will be shutdown.
There's no reason that the code couldn't suspend or hibernate instead of shutdown. Those functions simply don't work on my system which is why I call the shutdown command.
Copy the below code into a file called idle.py. Save this in your home directory (if you save it elsewhere you'll need to modify the upstart config file).
import os, os.path, sys
from pylms.server import Server
from pylms.player import Player
sc = Server(hostname="192.168.0.2", port=9090)
# Prints messages to screen and saves message in log file
log = '/home/username/idlelog'
f = open(log, 'a')
f.write(time.strftime("%Y-%m-%d %H:%M:%S") + ' ' + message + '\n')
print time.strftime("%Y-%m-%d %H:%M:%S") + ' ' + message
# Squeezeslave occasionally crashes on my server
# If this fails to find squeezeslave in the list of players the slave service is restarted.
# If you don't run a squeezeslave or don't have this problem delete this function.
foo = sc.get_player("00:00:00:00:00:01") # MAC of squeezeslave, replace as appropriate
os.system('service squeezeslave restart')
logprint('Slave service not running, service restarted')
# Checks to see if the xbmc screensaver is running
# Returns True when screensaver isn't running
fanart = '/home/username/Fanart'
currently_playing = urllib.urlopen('http://192.168.0.2:8080/xbmcCmds/xbmcHttp?command=getcurrentlyplaying').read()
logprint('Failed to connect to XBMC')
if fanart in currently_playing:
# Checks if the no sleep file exists. Use this as a manual override to prevent shutdown
path = '/home/username/nosleep'
logprint('Keepalive flag found: %s' % (path,))
logprint('No keepalive flag found')
# Check the status of all squeeze players attached the server
# Returns true if squeeze players are active
players = ["00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03"] # Replace with appropriate MACs
check_slave() # If you don't run any squeezeslaves this line can be deleted.
idle = 1
try: dp = sc.get_players()
except: logprint('Connection to SC failed')
for player in players:
foo = sc.get_player(player)
if foo.get_mode() not in ['stop','pause']: idle = 0
if idle == 1:
logprint('No squeezeboxes currently active')
logprint('Squeezeboxes currently playing')
# Check if any ssh connections are open to the server
connections = subprocess.Popen(['lsof','-i','-n','-P'], shell=False, stdout=subprocess.PIPE).communicate().split('\n')
lines = process.split('\n')
for line in connections:
if all(x in line for x in ['ssh','ESTABLISHED','username']):
logprint('%i SSH connection(s) to server active' % i)
logprint('No active ssh connections')
# Shutdown squeezeboxes and bring down the server
logprint('System has been idle for set time period, shutting down...')
sc.get_player("00:00:00:00:00:02").set_power_state(False) # This is to turn off any squeezeboxes. Replace MAC as appropriate.
os.system('shutdown -P now') # If hibernate or standby work this could be replaced with pm-suspend or pm-hibernate
# Main idle detection loop
idle_time = 60*20 # Amount of time system is to be idle before shutdown
sleep_time = 30 # Time between loop runs
if idle > idle_time:
idle = None
if (no_sleep() == False) and (xbmc_active() == False) and (sc_active() == False) and(ssh_active() == False):
if idle_begin == None: idle_begin = time.time()
idle = int(time.time() - idle_begin)
logprint('All systems idle, system has been idle for %s seconds\n\n' % str(idle))
The above code requires modification to get it working on your system:
Replace all instances of username with your username.
Replace MAC addresses as appropriate for your hardware.
Modify the paths for logging, fanart location and no_sleep to suit your system. Optional
Change the timers to suit. Note these are measured in seconds, not minutes.
Next create a the file /etc/init/idle.conf and paste the following into it.
# Idle detection
description "idle detection for XBMC & squeeze"
start on runlevel 
exec python /home/tungsten/idle.py
This will start the script 60 seconds after boot. You don't want it starting too quickly as the code requires the network to be active. It will exit if it looses the network.
To check if the script is running ok type:
sudo service idle status
This should return something like
idle start/running, process 1896
To monitor what the script is up to the following can be used:
tail -f /home/username/idlelog