- NEC monitor (sometimes good monitor is better then TV)
- Yamaha HTR-4065
- PC Zotac
- Evolveo DaulCoder DVB-T
- XBOX 360
All those are connected to network via ethernet and there is some way to monitor or even to remotely control, lets start one by one:
DVB-T and PC
As you might know one of the easiest way to check if device is up or down would be to ping it, however in case of audio/video systems it's bit hard because if they are in Standby mode they will answer to the ping.In some cases I don't even give the device permanent IP(DVB-T and PC). However there is the way to discover them via UPnP.
There is linux utility gssdp-discovery which use ssdp to discover UPnP capable device and as far as each device have unique identificator uuid we can check if device is running by checking if device is responding to ssdp discovery.
Below is small script which check based on uuid if device is running, which IP it was assigned and on which port UPnP is listening and record this to sqlite database.
#! /bin/bash #test if device can be discovered using SSDP #IMPORTANT return true if device is down testUPnP() { return `gssdp-discover -n 10 -t uuid:$1 | grep "resource available" | wc -l` } HFILE='/var/www/monitoring/device_upnp.list' DBFILE='/var/www/judo.db' if [[ -z $HFILE ]] then echo "$HFILE doesn't exist" exit 1 fi while read line do uuid=$(echo "$line" | cut -d, -f5) mac=$(echo "$line" | cut -d, -f4) new_polls=1 prev_status_polls=$(sqlite3 $DBFILE "SELECT cur_state,polls_in_state FROM dev_status WHERE mac = '$mac';") prev_status=$(echo "$prev_status_polls" | cut -d\| -f1) prev_polls=$(echo "$prev_status_polls" | cut -d\| -f2) #remeber testUPnP returns true if device is down if `testUPnP $uuid` then new_status="Down" if [ "$prev_status" == "Up" ] then sqlite3 $DBFILE "UPDATE devices SET ip = NULL, upnp_port = NULL WHERE mac = '$mac';" fi else new_status="Up" if [ "$prev_status" == "Down" ] then location=`gssdp-discover -n 10 -t uuid:$uuid | grep "Location:" | sed 's/ *Location: http:\/\///g'| cut -d/ -f1 | sed 's/\///g'` echo "$location" ip=$(echo "$location" | cut -d: -f1) upnp_port=$(echo "$location" | cut -d: -f2) echo "$ip:$upnp_port" sqlite3 $DBFILE "UPDATE devices SET ip = '$ip', upnp_port = '$upnp_port' WHERE mac = '$mac';" fi fi if [ "$prev_status" == "$new_status" ] then new_polls=$((prev_polls + 1)) fi sqlite3 $DBFILE "UPDATE dev_status SET cur_state = '$new_status', polls_in_state = '$new_polls' WHERE mac = '$mac';" echo "$uuid,$mac,$new_status,$prev_status,$new_polls" done < ${HFILE}
Yamaha HTR-4065
Even though Yamaha have UPnP we will rather use YNC to get the status, because we also want to know which input is actually active. Below is python script which finds the status and input and store that in sqlite database.
#! /usr/bin/python import sqlite3 import sys import httplib, urllib import re from xml.dom import minidom from xml.parsers.expat import ExpatError conn = httplib.HTTPConnection("10.0.0.36:80") xml_req_power = '<?xml version="1.0" encoding="utf-8"?><YAMAHA_AV cmd="GET"><Main_Zone><Power_Control><Power>GetParam</Power></Power_Control></Main_Zone></YAMAHA_AV>' xml_req_input = '<?xml version="1.0" encoding="utf-8"?><YAMAHA_AV cmd="GET"><Main_Zone><Input><Input_Sel>GetParam</Input_Sel></Input></Main_Zone></YAMAHA_AV>' params = urllib.urlencode({'q': 'set'}) headers = { 'Content-Type': 'application/xml', "Content-Length": "%d" % len(xml_req_power)} conn.request("POST", "/YamahaRemoteControl/ctrl", "", headers) conn.send(xml_req_power) xmldoc = minidom.parseString(conn.getresponse().read()) xml_input_element = xmldoc.getElementsByTagName('Power') #print xml_input_element[0] for node in xml_input_element: power_status = node.firstChild.nodeValue conn.close() #================================= #Geting input headers = { 'Content-Type': 'application/xml', "Content-Length": "%d" % len(xml_req_input)} conn.request("POST", "/YamahaRemoteControl/ctrl", "", headers) conn.send(xml_req_input) #response = conn.getresponse() #print response.status, response.reason #response_data = response.read() #print response_data xmldoc = minidom.parse(conn.getresponse()) xml_input_element = xmldoc.getElementsByTagName('Input_Sel') for node in xml_input_element: av_input = node.firstChild.nodeValue conn.close() print "Status\t\tAV Input" print power_status + "\t\t" + av_input select_query = "SELECT cur_state,polls_in_state,input FROM dev_status WHERE mac = '00:a0:de:92:6b:78';" #print update_query new_polls=1 judodb = sqlite3.connect('/var/www/judo.db') cur = judodb.cursor() rows = cur.execute(select_query) for row in rows: prev_state = row[0] prev_polls = row[1] prev_input = row[2] if prev_state == power_status and prev_input == av_input: new_polls = prev_polls + 1 print prev_state + "\t\t" + str(new_polls) update_query = "UPDATE dev_status SET input = \'" + av_input + "\', cur_state = \'" + power_status + "\', polls_in_state = " + str(new_polls) + " WHERE mac = '00:a0:de:92:6b:78'" cur.execute(update_query) judodb.commit() judodb.close()
NEC
NEC responds to ping in Standby and of course don't have UPnP, however the NEC remote control over ethernet let us find out the actual status.Below python script check the status and store it in sqlite database.
#! /usr/bin/python import sys import socket import sqlite3 monitor_ip = '10.0.0.35' port = 7142 buffer_size = 2048 data_on = '\x01\x30\x41\x30\x41\x30\x36\x02\x30\x31\x44\x36\x03\x74\x0d' new = socket.socket(socket.AF_INET, socket.SOCK_STREAM) new.connect((monitor_ip, port)) new.send(data_on) data = '' #message is split over several packets so need to get all content, message we expect have 25bytes packet = new.recv(buffer_size) data += packet while len(data) < 25 or not packet: packet = new.recv(buffer_size) data += packet new.close() #Reponse OFF #00AB120200D60000040004q #Response ON #00AB120200D60000040001t off_response = b'\x30\x30\x41\x42\x31\x32\x30\x32\x30\x30\x44\x36\x30\x30\x30\x30\x30\x34\x30\x30\x30\x34' on_response = bytes("\x30\x30\x41\x42\x31\x32\x30\x32\x30\x30\x44\x36\x30\x30\x30\x30\x30\x34\x30\x30\x30\x31") #there seems to be some not printable chars because of packet fragmentation #using index is dirty hack but don't care if data[23] == off_response[21]: power_status = "Off" elif data[23] == on_response[21]: power_status = "On" else: power_status = "Unknown" #Now we need to update sqlite with status select_query = "SELECT cur_state,polls_in_state FROM dev_status WHERE mac = '00:25:5c:2e:36:39';" #print update_query new_polls=1 judodb = sqlite3.connect('/var/www/judo.db') cur = judodb.cursor() rows = cur.execute(select_query) for row in rows: prev_state = row[0] prev_polls = row[1] if prev_state == power_status: new_polls = prev_polls + 1 print prev_state + "\t\t" + str(new_polls) update_query = "UPDATE dev_status SET cur_state = \'" + power_status + "\', polls_in_state = " + str(new_polls) + " WHERE mac = '00:25:5c:2e:36:39'" cur.execute(update_query) judodb.commit() judodb.close()
XBOX 360
For monitoring of XBOX 360 I've have to use arping and UPnP, because of limitation of XBOX 360 OS more can be find here including the script.
Automatic Switch Off
Now as we have ways of getting status we just use crontab to run the scripts each 10 minutes and to get the status and store it in to sqlite database. We are storing also how many time we see it consecutively in current status. (polls_in_state column in table below)
sqlite> .schema dev_status CREATE TABLE dev_status(mac TEXT, cur_state TEXT, polls_in_state NUM, input TEXT); sqlite> select * from dev_status; 00:25:5c:2e:36:39|On|3|unknown 00:a0:de:92:6b:78|On|3|HDMI1 00:ce:39:b5:48:9e|Up|4|unknown 7c:1e:52:c2:6c:a7|Up|3|unknown 00:01:2e:3a:a3:b2|Down|1214|unknownLast thing we have to do is set crontab to run script, which check status based on the status of devices finds what is running and if no device coresponding to AV receiver input is running, (means that AV receiver is running for nothing) it will switch it off. Same for NEC.
crontab -l #Monitoring of home network */10 * * * * /var/www/monitoring/judo_icmp_monitoring &> /dev/null */10 * * * * /var/www/monitoring/judo_upnp_monitoring &> /dev/null */10 * * * * /var/www/monitoring/HTR-4065_State.py &> /dev/null */10 * * * * /var/www/monitoring/NECStatus.py &> /dev/null */10 * * * * /var/www/monitoring/XBOX360Status.sh &> /dev/null 5,15,25,35,45,55 * * * * /var/www/monitoring/autostop >> /media/nas/public/autostop.log
autostop script
#! /bin/bash HTR_MAC='00:a0:de:92:6b:78' NEC_MAC='58:C2:32:97:3A:48' Evolve_MAC='00:ce:39:b5:48:9e' XBOX_MAC='7c:1e:52:c2:6c:a7' Zotac_MAC='00:01:2e:3a:a3:b2' CGIDIR='/var/www/cgi-bin' DBFILE='/var/www/judo.db' getDeviceStatus(){ SQLQUERY="SELECT cur_state,polls_in_state,input FROM dev_status WHERE mac = '$1';" SQLRESULT=`sqlite3 $DBFILE "$SQLQUERY" | sed 's/|/ /g;'` echo "$SQLRESULT" } ACTION='No action' echo -n "" read HTR_STATUS HTR_POLLS_IN_STATUS HTR_INPUT <<< `getDeviceStatus $HTR_MAC` echo -n " $HTR_STATUS $HTR_POLLS_IN_STATUS $HTR_INPUT " #Example of output #00:a0:de:92:6b:78|On|51|HDMI1 if [[ $HTR_STATUS == "On" && $HTR_POLLS_IN_STATUS -gt 3 ]] then case $HTR_INPUT in "HDMI1") #echo "Check Evolve status" read Evolve_STATUS Evolve_POLLS_IN_STATUS Evolve_INPUT <<< `getDeviceStatus $Evolve_MAC` echo -n "$Evolve_STATUS $Evolve_POLLS_IN_STATUS $Evolve_INPUT " if [[ $Evolve_STATUS == "Down" && $Evolve_POLLS_IN_STATUS -gt 3 ]] then $CGIDIR/NECOff.py $CGIDIR/HTR-4065_Off.py ACTION="Switching HTR-4065 and NEC off" fi ;; "HDMI2") #echo "Check XBOX 360 status" read XBOX_STATUS XBOX_POLLS_IN_STATUS XBOX_INPUT <<< `getDeviceStatus $XBOX_MAC` echo -n "$XBOX_STATUS $XBOX_POLLS_IN_STATUS $XBOX_INPUT " if [[ $XBOX_STATUS == "Down" && $XBOX_POLLS_IN_STATUS -gt 3 ]] then $CGIDIR/NECOff.py $CGIDIR/HTR-4065_Off.py ACTION="Switching HTR-4065 and NEC off" fi ;; "HDMI3") #echo "Checking PC Zotac Status" read Zotac_STATUS Zotac_POLLS_IN_STATUS Zotac_INPUT <<< `getDeviceStatus $Zotac_MAC` echo -n "$Zotac_STATUS $Zotac_POLLS_IN_STATUS $Zotac_INPUT " if [[ $Zotac_STATUS == "Down" && $Zotac_POLLS_IN_STATUS -gt 3 ]] then $CGIDIR/NECOff.py $CGIDIR/HTR-4065_Off.py ACTION="Switching HTR-4065 and NEC off" fi ;; esac else #AV reciever is Standby for more then hour and NEC is On for if [[ ! $HTR_STATUS == "On" ]] then read NEC_STATUS NEC_POLLS_IN_STATUS NEC_INPUT <<< `getDeviceStatus $NEC_MAC` echo -n "$NEC_STATUS $NEC_POLLS_IN_STATUS $NEC_INPUT " if [[ $HTR_POLLS_IN_STATE -gt 6 && $NEC_STATUS == "On" && $NEC_POLLS_IN_STATUS -gt 3 ]] then $CGIDIR/NECoff.py ACTION="Switching NEC Off" fi fi fi echo -n "$ACTION " echo "" #HTR-4065 HDMI1 : Evolve Down,3 => OFF: NEC, HTR-4065 #HTR-4065 HDMI2 : XBOX_360 Down,3 => OFF: NEC, HTR-4065 #HTR-4065 HDMI3 : PC_Zotac Down,3 => OFF: NEC, HTR-4065 #HTR-4065 HDMI4 : Not Assigned