El Geek

Ez a blog nem igazi blog, nem érdemes RSS feedre tenni, mert csak a számítástechnikában szerzett tapasztalataim rögzítése - nem találtam neki jobb helyet. Rendszeresen frissülni sem fog, nekem lesz jó visszanyúlni egy-egy elfeledett probléma esetén, illetve a gugli remélhetőleg idevezeti az embert, ha hasonló problémája lesz, mint nekem. A blog írásához ugyanis pont az vezetett, hogy vannak problémák, amelyekre a gugli nem első helyen szállítja a megoldást, kicsit jobban meg kell túrni értük az internetet.

Friss topikok

Routerek, scriptek, ki- és bekapcsolás

2013.03.22. 18:10 | 765mb | Szólj hozzá!

Címkék: windows script Linux Router WoL Tomato Tcl/Tk

Építgetve az otthoni rendszert fogalmazódott meg bennem a következő feladat: jó lenne, ha a TV bekapcsolásakor a számítógép is automatikusan bekapcsolna, nem kellene külön foglalkozni vele. Véletlenül a kezembe akadt egy okosítható router. Ezalatt azt értem, hogy a firmware-e cserélhető volt DD-WRT-re, Tomato-ra, vagy barátaira. Egy ajánlásnak köszönhetően a Tomatot választottam. Be lehet rá SSH-zni és lehet szkripteket alkotni.

Egy okosTV válaszol a pingre. Így mindössze annyi volt a feladat, hogy néhány másodpercenként megnézze a router, hogy él-e a TV és ha igen, akkor keltse fel Wake-On-LAN segítségével a számítógépet. Nem lenne baj az sem, ha esetleg az IP-cím, a felkeltendő gép MAC címe, vagy a pihenési idő változtatható lenne, így ezeket az adatokat konfig fájlból töltöm be minden egyes körben.

Ez a script így néz ki:

while [ 1 ]
do
  IP=`grep IP2ping /tmp/home/root/TV.conf | awk '{print $2}'`
  MAC=`grep MAC2wake /tmp/home/root/TV.conf | awk '{print $2}'`
  TIME=`grep time2sleep /tmp/home/root/TV.conf | awk '{print $2}'`
  live=`ping -c1 -W2 $IP | grep received | awk '{print $4}'`
  date > /tmp/home/root/TV.state
  if [ $live -eq 1 ]; then
    #ether-wake $MAC
    echo "TV is ON" >> /tmp/home/root/TV.state
  else
    echo "TV is OFF" >> /tmp/home/root/TV.state
  fi
  sleep $TIME
done

A config fájl pedig így:

IP2ping 192.168.1.81
MAC2wake D4:6F:24:A2:11:52
time2sleep 10

Ez nagyon jó, csak sajnos újraindításkor a /tmp alatti dolgok eltűnnek, hiszen RAMból fut a holmi. A Tomato lehetőséget ad viszont arra, hogy megadjunk egy induláskor lefuttatható szkriptet (Administration -> Scripts). Így ahelyett, hogy belegányoltam volna valami tartósabb helyre azt választottam, hogy az induláskor lefutó script hozza létre a szükséges file-okat:

#creating conf file
echo "IP2ping 192.168.1.81" > /tmp/home/root/TV.conf
echo "MAC2wake D4:6F:24:A2:11:52" >> /tmp/home/root/TV.conf
echo "time2sleep 10" >> /tmp/home/root/TV.conf

#creating script
echo "while [ 1 ]" > /tmp/home/root/TV.sh
echo "do" >> /tmp/home/root/TV.sh
echo "  IP=\`grep IP2ping /tmp/home/root/TV.conf | awk '{print \$2}'\`" >> /tmp/home/root/TV.sh
echo "  MAC=\`grep MAC2wake /tmp/home/root/TV.conf | awk '{print \$2}'\`" >> /tmp/home/root/TV.sh
echo "  TIME=\`grep time2sleep /tmp/home/root/TV.conf | awk '{print \$2}'\`" >> /tmp/home/root/TV.sh
echo "  live=\`ping -c1 -W2 \$IP | grep received | awk '{print \$4}'\`" >> /tmp/home/root/TV.sh
echo "  date > /tmp/home/root/TV.state" >> /tmp/home/root/TV.sh
echo "  if [ \$live -eq 1 ]; then" >> /tmp/home/root/TV.sh
echo "    #ether-wake \$MAC" >> /tmp/home/root/TV.sh
echo "    echo \"TV is ON\" >> /tmp/home/root/TV.state" >> /tmp/home/root/TV.sh
echo "  else" >> /tmp/home/root/TV.sh
echo "    echo \"TV is OFF\" >> /tmp/home/root/TV.state" >> /tmp/home/root/TV.sh
echo "  fi" >> /tmp/home/root/TV.sh
echo "  sleep \$TIME" >> /tmp/home/root/TV.sh
echo "done" >> /tmp/home/root/TV.sh

#launching script
chmod +x /tmp/home/root/TV.sh
/bin/sh /tmp/home/root/TV.sh &

Ha bekapcsoltuk, jó lenne ki is kapcsolni. Ezt különböző feltételekhez érdemes kötni. Pl. ha a TV kikapcsolt. Ezt a fenti script fordítottjával el lehet érni, ezzel most nem foglalkoznék. De a HTPC-t nem csak DLNA server-ként, hanem esetleg bejelentkezem rá valamiért távolról. Nem lenne jó tehát, ha egyszerűen csak kikapcsolna a TV-vel együtt. Figyelni kellene arra, hogy van-e felhasználói aktivitás. A Windows tudtommal nem nyújt olyan remek script futtatási lehetőségeket magától, mint a Linux, ezért vettem igénybe a Windows-on is futtatható Tcl/Tk segítségét. Ez alapvetően nem Windows specifikus, hiszen a scriptek hordozhatóak, futnak Linuxon is. De szerencsére letölthető hozzá Windows API-t használó csomag is innen: http://twapi.magicsplat.com/ Így már remek Windows specifikus scripteket írhatunk a jól ismert Tcl/Tk nyelven. Mivel az említett TWAPI tartalmaz egy a billentyűzet, illetve az egér parkolásának idejét számláló függvényt, továbbá lehetséges power related függvények hívása is, a dolog innen nagyon leegyszerűsödik. Az alábbi script 10 másodpercnyi tétlenkedés után aludni küldi a gépet. Amit aztán a router felkelt, ha bekapcsol a TV :-)

package require twapi
package require Tclx

set idle_time 0

while { $idle_time < 10000 } {
  set idle_time [twapi::get_input_idle_time]
  puts "$idle_time"
  sleep 2
}

twapi::suspend_system -state standby

Update: kicsit bővült funkcionalitásban a TCL szkript is és a router induló szkriptje is. Fontosabb tanulságok a következők.

  • A Tomatonak saját kulcspár generátora van, ami dropbearkey névre hallgat. Innen jöttem rá: http://tomatousb.org/forum/t-320537Ez a kulcspárosdi ahhoz kell, hogy a Tomato szkriptből be tudjon lépni a Brazosra és módosítgatni dolgokat, ne kelljen jelszó. Sajnos a generált magánkulcs  nem kiiratható, így nem tudtam betenni az induló szkriptbe sem.
  • Így arra jutottam, hogy felteszem valahová, töltse le a Tomato indulásakor. Ennek során derült ki, hogy a Tomato indulásakor még nem éri el a netet, így a wget nem működik, ez indokolja a sleepes megoldást a key_downloader segédscriptnél.
  • SSH-val belépve az XBMC nem indul el, ezért kell a bonyolult konfig fájlos megoldás a TCL szkript részeként
  • A szkriptnek grafikus felülete lett és minden beállítást elment egy-egy konfig fájlba is, így indításkor az előzőleg beállított értékeket kapja. A külön konfig fájlok értelme az, hogy SSHból is könnyen konfigurálható a működés.
  • A TV néha egy-egy pingre nem válaszol, ezért a kimaradó válasz után nem érdemes egyből kikapcsolni a gépet. Ráadásul ha megy a TV, akkor ilyenkor a router vissza is kapcsolja :-) Ezért vezettem be a TV kikapcsolás utáni idle paraméterét.
  • Kikapcsoláskor nem azonnal egyértelmű, hogy nem válaszol a TV, ilyenkor van a request timeout. Ekkor a ping hibával tér vissza, amit le kell kezelni (catch)
  • Ha a TCL szkriptben GUI is van, akkor az értékeket frissítő rész nem mehet egyszerűen egy while ciklusként, mert ez azt eredményezi, hogy a GUI sosem kerül kirajzolásra, hiszen nem kerülünk ki a while ciklusból és érünk a GUI main loopjához. Ezért az after függvénnyel időzítve meghívom a korábbi while ciklus törzsét. Ez azért nagyszerű, mert a meghívott függvény végén is használhatom ezt a fajta időzítést, mert nem úgy működik, hogy egy stackben egyre mélyebbre és mélyebbre kerülünk, hanem az after-rel történő függvényhívás teljesen új hívásnak fog számítani. Az újrahívás gyakoriságát így ráadásul paramétereztem, hát miért ne? :-)

Így a router induló szkriptje a következő:

#creating conf file
echo "IP2ping 192.168.1.71" > /tmp/home/root/TV.conf
echo "MAC2wake D4:6F:24:A2:11:52" >> /tmp/home/root/TV.conf
echo "time2sleep 10" >> /tmp/home/root/TV.conf

#creating script
echo "while [ 1 ]" > /tmp/home/root/TV.sh
echo "do" >> /tmp/home/root/TV.sh
echo "  IP=\`grep IP2ping /tmp/home/root/TV.conf | awk '{print \$2}'\`" >> /tmp/home/root/TV.sh
echo "  MAC=\`grep MAC2wake /tmp/home/root/TV.conf | awk '{print \$2}'\`" >> /tmp/home/root/TV.sh
echo "  TIME=\`grep time2sleep /tmp/home/root/TV.conf | awk '{print \$2}'\`" >> /tmp/home/root/TV.sh
echo "  live=\`ping -c1 -W2 \$IP | grep received | awk '{print \$4}'\`" >> /tmp/home/root/TV.sh
echo "  date > /tmp/home/root/TV.state" >> /tmp/home/root/TV.sh
echo "  if [ \$live -eq 1 ]; then" >> /tmp/home/root/TV.sh
echo "    ether-wake \$MAC" >> /tmp/home/root/TV.sh
echo "    echo \"TV is ON\" >> /tmp/home/root/TV.state" >> /tmp/home/root/TV.sh
echo "  else" >> /tmp/home/root/TV.sh
echo "    echo \"TV is OFF\" >> /tmp/home/root/TV.state" >> /tmp/home/root/TV.sh
echo "  fi" >> /tmp/home/root/TV.sh
echo "  sleep \$TIME" >> /tmp/home/root/TV.sh
echo "done" >> /tmp/home/root/TV.sh

#launching script
chmod +x /tmp/home/root/TV.sh
/bin/sh /tmp/home/root/TV.sh &

#key pair based logging in to Brazos
mkdir /tmp/home/root/.ssh
echo "sleep 60" > /tmp/home/root/key_downloader
echo "wget http://some.server.hu/~elgeek/brazos/id_rsa -O /tmp/home/root/.ssh/id_rsa" >> /tmp/home/root/key_downloader
echo "chmod 600 /tmp/home/root/.ssh/id_rsa" >> /tmp/home/root/key_downloader
chmod +x /tmp/home/root/key_downloader
/bin/sh /tmp/home/root/key_downloader &

#brazos launcher script
echo "ether-wake D4:6F:24:A2:11:52" > /tmp/home/root/b
chmod +x /tmp/home/root/b

#XBMC launcher script
echo "ssh -i .ssh/id_rsa 192.168.1.72 \"echo 1 > d:\brazoscontrol\config_launch_xbmc.txt\" " > /tmp/home/root/x
chmod +x /tmp/home/root/x

A HTPCn futó TCL szkript pedig a következő:

package require twapi
package require Tclx
package require Tk

#it may happen that TV does not respond for one or two ping request
#if defined idle time has elapsed this means that TV script turns computer off immediately
#therefore some time has to be elapsed after turning TV off before computer can be turned off

#config files
set config_prevent "d:/BrazosControl/config_prevent_sleep.txt"
set config_updateinterval "d:/BrazosControl/config_updateinterval.txt"
set config_timetoidle "d:/BrazosControl/config_timetoidle.txt"
set config_timetoidle_after_tvoff "d:/BrazosControl/config_timetoidle_after_tvoff.txt"
set config_tvip "d:/BrazosControl/config_tvip.txt"
set config_xbmc "d:/BrazosControl/config_launch_xbmc.txt"

set standby_instead_of_shutdown 0
#whether torrent should be turned off separately
set turn_off_torrent 0

##CONFIG VARIABLES
#prevent script from turning computer off
set prevent_sleep 0

#update interval in secundum
set delta_t_s 0
set delta_t_ms 0

#idle time before turning off
set time_to_idle_in_minutes 0
set time_to_idle 0

#idle time after TV is turned off
set time_to_idle_in_minutes_after_tvoff 0
set time_to_idle_after_tvoff 0

#ip of TV
set tvip 0

set idle_time_text "0/$time_to_idle"
set idle_time_after_tvoff_text "0/$time_to_idle_after_tvoff"
set tvdescr ""

set idle_time 0

set tv_off_offset 0
set tv_prev_off 2

proc update_prevent_control {} {
  global config_prevent
  global prevent_sleep

  set fileId [open $config_prevent "w"]
  puts -nonewline $fileId $prevent_sleep
  close $fileId
}

proc update_update_interval {} {
  global delta_t_s
  global delta_t_ms
  global config_updateinterval

  set delta_t_ms [expr $delta_t_s*1000]
  puts "New update interval in ms is $delta_t_ms"

  set fileId [open $config_updateinterval "w"]
  puts -nonewline $fileId $delta_t_s
  close $fileId
}

proc update_timetoidle {} {
  global time_to_idle_in_minutes
  global time_to_idle
  global config_timetoidle

  set time_to_idle [expr $time_to_idle_in_minutes*60*1000]
  puts "New idle time in ms is $time_to_idle"

  set fileId [open $config_timetoidle "w"]
  puts -nonewline $fileId $time_to_idle_in_minutes
  close $fileId
}

proc update_timetoidle_after_tvoff {} {
  global time_to_idle_in_minutes_after_tvoff
  global time_to_idle_after_tvoff
  global config_timetoidle_after_tvoff

  set time_to_idle_after_tvoff [expr $time_to_idle_in_minutes_after_tvoff*60*1000]
  puts "New idle time after TV off in ms is $time_to_idle_after_tvoff"

  set fileId [open $config_timetoidle_after_tvoff "w"]
  puts -nonewline $fileId $time_to_idle_in_minutes_after_tvoff
  close $fileId
}

proc update_tvip {} {
  global tvip
  global config_tvip

  puts "New IP of TV is $tvip"

  set fileId [open $config_tvip "w"]
  puts -nonewline $fileId $tvip
  close $fileId
}

wm title . "Brazos control"
grid [ttk::frame .c -padding "3 3 12 12"] -column 0 -row 0 -sticky nwes
grid columnconfigure . 0 -weight 1; grid rowconfigure . 0 -weight 1

#titles
set label_tv_state "TV state:"
set label_idle "Overall idle time (ms):"
set label_idle_after_tvoff "Idle time since TV is off (ms):"
set label_prevent "Prevent script from turning computer off:"
set label_update "State update interval (s):"
set label_timetoidle "Idle time before turning computer off (min):"
set label_timetoidle_after_tvoff "Idle time after TV turned off (min):"
set label_tvip "IP address of TV:"

grid [ttk::label .c.l_tv -textvariable label_tv_state] -column 1 -row 1 -sticky we
grid [ttk::label .c.l_idle -textvariable label_idle] -column 1 -row 2 -sticky we
grid [ttk::label .c.l_idle_after_tvoff -textvariable label_idle_after_tvoff] -column 1 -row 3 -sticky we
grid [ttk::label .c.l_prevent -textvariable label_prevent] -column 1 -row 4 -sticky we
grid [ttk::label .c.l_update -textvariable label_update] -column 1 -row 5 -sticky we
grid [ttk::label .c.l_timetoidle -textvariable label_timetoidle] -column 1 -row 6 -sticky we
grid [ttk::label .c.l_timetoidle_after_tvoff -textvariable label_timetoidle_after_tvoff] -column 1 -row 7 -sticky we
grid [ttk::label .c.l_tvip -textvariable label_tvip] -column 1 -row 8 -sticky we

#values
grid [ttk::label .c.v_tv -textvariable tvdescr] -column 2 -row 1  -sticky we
grid [ttk::label .c.v_idle -textvariable idle_time_text -width 20] -column 2 -row 2 -sticky we
grid [ttk::label .c.v_idle_after_tvoff -textvariable idle_time_after_tvoff_text -width 20] -column 2 -row 3 -sticky we
grid [ttk::checkbutton .c.v_prevent -variable prevent_sleep -command update_prevent_control] -column 2 -row 4 -sticky we
grid [ttk::entry .c.v_update -textvariable delta_t_s -width 3] -column 2 -row 5 -sticky we
grid [ttk::entry .c.v_timetoidle -textvariable time_to_idle_in_minutes -width 3] -column 2 -row 6 -sticky we
grid [ttk::entry .c.v_timetoidle_after_tvoff -textvariable time_to_idle_in_minutes_after_tvoff -width 3] -column 2 -row 7 -sticky we
grid [ttk::entry .c.v_tvip -textvariable tvip -width 9] -column 2 -row 8 -sticky we

bind .c.v_update <KeyPress> {
  if {[string match "Return" %K]} {
    update_update_interval
  }
}

bind .c.v_timetoidle <KeyPress> {
  if {[string match "Return" %K]} {
    update_timetoidle
  }
}

bind .c.v_timetoidle_after_tvoff <KeyPress> {
  if {[string match "Return" %K]} {
    update_timetoidle_after_tvoff
  }
}

bind .c.v_tvip <KeyPress> {
  if {[string match "Return" %K]} {
    update_tvip
  }
}

foreach w [winfo children .c] {grid configure $w -padx 5 -pady 5}

proc update_cycle {} {
  global turn_off_torrent

  global config_prevent
  global config_updateinterval
  global config_timetoidle
  global config_timetoidle_after_tvoff
  global config_tvip
  global config_xbmc

  global prevent_sleep
  global time_to_idle
  global time_to_idle_in_minutes
  global time_to_idle_after_tvoff
  global time_to_idle_in_minutes_after_tvoff
  global delta_t_ms
  global delta_t_s
  global tvip

  global idle_time

  global idle_time_text
  global idle_time_after_tvoff_text
  global tvdescr

  global standby_instead_of_shutdown

  global tv_off_offset
  global tv_prev_off

  ##REFRESH CONFIG
  #prevent script from turning computer off
  set fileId [open $config_prevent "r"]
  set prevent_sleep [read -nonewline $fileId]
  close $fileId

  #update interval in secundum
  set fileId [open $config_updateinterval "r"]
  set delta_t_s [read -nonewline $fileId]
  close $fileId
  set delta_t_ms [expr $delta_t_s*1000]

  #idle time before turning off
  set fileId [open $config_timetoidle "r"]
  set time_to_idle_in_minutes [read -nonewline $fileId]
  close $fileId
  set time_to_idle [expr $time_to_idle_in_minutes*60*1000]

  #idle time after TV is turned off
  set fileId [open $config_timetoidle_after_tvoff "r"]
  set time_to_idle_in_minutes_after_tvoff [read -nonewline $fileId]
  close $fileId
  set time_to_idle_after_tvoff [expr $time_to_idle_in_minutes_after_tvoff*60*1000]

  #ip of TV
  set fileId [open $config_tvip "r"]
  set tvip [read -nonewline $fileId]
  close $fileId

  #XBMC launch - if needed
  set fileId [open $config_xbmc "r"]
  set xbmc [read -nonewline $fileId]
  close $fileId
  if {$xbmc} {
    twapi::create_process "c:/Program Files (x86)/XBMC/XBMC.exe"
    set fileId [open $config_xbmc "w"]
    puts -nonewline $fileId "0"
    close $fileId
  }

  #if utorrent does not run - launch it
  #it is allowed as after killing it and going to standby system waits enough time
  set proclist [ twapi::get_process_ids -name "uTorrent.exe" ]
  set proclistlength [ llength $proclist ]

  if { $proclistlength == 0 } {
    twapi::create_process "C:/Users/elGeek/AppData/Roaming/uTorrent/uTorrent.exe"
    #wait enough time to let uTorrent start
    sleep 15
  }

  #is TV OFF?
  set tvoff 0
  if { [catch {set tvoff [expr 1-[regexp "bytes=32 time=" [eval exec c:/Windows/System32/ping -n 1 $tvip]]]} fid] } {
    set tvdescr "N/A"
    puts "Pinging TV was not successful. $fid"
  } else {
    if {$tvoff==1} {
      set tvdescr "OFF"
      if {$tv_prev_off == 0} {
        set tv_off_offset [twapi::get_input_idle_time]
        puts "TV is just turned off"
      }
    } else {
      set tvdescr "ON"
    }
  }
  set tv_prev_off $tvoff

  #register idle time
  set prev_idle_time $idle_time
  set idle_time [twapi::get_input_idle_time]

  #if idle time decreased, no offset is needed
  #else "after tv time" would be engative
  if {$prev_idle_time > $idle_time} {
   set tv_off_offset 0
  }

  set idle_time_text "$idle_time/$time_to_idle"
  if {$tvoff == 0} {
    set idle_time_after_tvoff_text "0/$time_to_idle_after_tvoff"
  } else {
    set idle_time_after_tvoff_text "[expr $idle_time-$tv_off_offset]/$time_to_idle_after_tvoff"
  }

  #if TV is OFF AND Brazos is idling for a while then go sleep
  if {($idle_time > $time_to_idle) && $tvoff && ([expr $idle_time-$tv_off_offset]>$time_to_idle_after_tvoff)} {
    if {$turn_off_torrent} {
      #uTorrent should be killed as it prevents computer from going to sleep
      set proclist [ twapi::get_process_ids -name "uTorrent.exe" ]
      foreach e $proclist {
        twapi::end_process $e
      }
      #wait enough  to let torrent be killed for sure
      sleep 10
    }

    #and than go to sleep
    if { $prevent_sleep == 0} {
      if {$standby_instead_of_shutdown} {
        twapi::suspend_system -state standby

        #after waking from sleep wait some time to let the system be stable
        sleep 20
      } else {
        twapi::shutdown_system
      }
    }
    puts "Brazos would have been switched off."

  } else {
    puts "Brazos would have been left running. TVoff: $tvoff idle: $idle_time ($time_to_idle) prevent: $prevent_sleep"
  }

  #wait before next cycle
  #puts "Sleeping $delta_t_ms before next cycle"
  after $delta_t_ms update_cycle
}

after 100 update_cycle

A bejegyzés trackback címe:

https://elgeek.blog.hu/api/trackback/id/tr565162999

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása