DbusScripts
m (→turn to 2g when connected to wifi network) |
(→Running dbus-scripts on the session bus) |
||
(40 intermediate revisions not shown) | |||
Line 1: | Line 1: | ||
- | + | [http://www.cobb.uk.net/770/#dbus-scripts dbus-scripts] is a daemon that can execute a command when various actions occurs on D-Bus. Some D-Bus signals you can watch for are : | |
- | + | ||
- | [http://www.cobb.uk.net/770/#dbus-scripts dbus-scripts] is a daemon | + | |
- | that can execute a command when various | + | |
- | + | ||
* keyboard slide | * keyboard slide | ||
Line 9: | Line 5: | ||
* connection or disconnection from a network | * connection or disconnection from a network | ||
* sms or phone call arriving | * sms or phone call arriving | ||
+ | * desktop started (ie. after boot is completed) | ||
+ | * bnep or usb network started or stopped (use to configure or run firewalls etc) | ||
- | You can find dbus-scripts in [ | + | You can find dbus-scripts in [[extras-devel]] (so be careful, your [[Nokia N900|N900]] could crash). |
- | (so be careful, | + | |
- | + | ||
- | + | ||
- | + | ||
- | There's | + | There's some documentation in the [http://www.cobb.uk.net/770/dbus-scripts-example dbus-scripts config file] |
- | [http:// | + | |
- | + | ||
- | + | ||
+ | There's also a configurator program [http://maemo.org/packages/view/dbus-scripts-settings/ dbus-scripts-settings] written by Matan Ziv-Av. You can use dbus-scripts-settings settings to configure dbus-scripts or just to browse. | ||
Thanks to Graham and Matan. | Thanks to Graham and Matan. | ||
== Configuration == | == Configuration == | ||
- | configuration files for dbus-scripts are in /etc/dbus-scripts.d and | + | === Basic configuration === |
- | you can find some documentation in | + | |
- | /etc/dbus-scripts.d/dbus-scripts-example : | + | Configuration files for dbus-scripts are in <code>/etc/dbus-scripts.d</code> and you can find some documentation in <code>/etc/dbus-scripts.d/dbus-scripts-example</code>: |
# Example of dbus-scripts control file | # Example of dbus-scripts control file | ||
Line 53: | Line 45: | ||
/some/script * * com.nokia.icd status_changed * WLAN_INFRA | /some/script * * com.nokia.icd status_changed * WLAN_INFRA | ||
- | * this one will match all events for wlan network id of | + | * this one will match all events for wlan network id of 91f493fb-7c89-4fc6-ac2c-b822923dde45, it's mine |
- | 91f493fb-7c89-4fc6-ac2c-b822923dde45, it's mine | + | |
- | + | You can find the wlan id with the command | |
- | /system/osso/connectivity/IAP | + | gconftool-2 -R /system/osso/connectivity/IAP |
/some/script * * com.nokia.icd status_changed 91f493fb-7c89-4fc6-ac2c-b822923dde45 WLAN_INFRA | /some/script * * com.nokia.icd status_changed 91f493fb-7c89-4fc6-ac2c-b822923dde45 WLAN_INFRA | ||
Line 64: | Line 55: | ||
/some/script * * com.nokia.icd status_changed 91f493fb-7c89-4fc6-ac2c-b822923dde45 WLAN_INFRA CONNECTED | /some/script * * com.nokia.icd status_changed 91f493fb-7c89-4fc6-ac2c-b822923dde45 WLAN_INFRA CONNECTED | ||
+ | |||
+ | === dbus-scripts-settings === | ||
+ | |||
+ | There's also a configurator program [http://maemo.org/packages/view/dbus-scripts-settings/dbus-scripts-settings] written by Matan Ziv-Av. You can use dbus-scripts-settings settings to configure dbus-scripts or just to browse. | ||
+ | |||
+ | :As of 2010-07-16, dbus-scripts-settings is missing a dependency on package 'gnome-python-dev'. If the program crashes and at command line you get the error | ||
+ | |||
+ | <source lang="text"> | ||
+ | Traceback (most recent call last): | ||
+ | File "/usr/bin/dbus-scripts-settings", line 5, in <module> | ||
+ | from gnome import gconf | ||
+ | ImportError: No module named gnome | ||
+ | </source> | ||
+ | |||
+ | :then the fix is to install gnome-python-dev. [[User:magick777|magick777]] 14:09, 16 July 2010 (UTC) | ||
+ | |||
+ | === dbus-scripts daemon options === | ||
+ | |||
+ | ==== Running dbus-scripts on the session bus ==== | ||
+ | |||
+ | By default, the dbus-scripts daemon listens on the system message bus, which reports system events but not those from the user session. If you want to access events such as an application being launched, you'll need to configure the daemon to listen to the session bus as well. You'll probably want to run a second instance of dbus-scripts without breaking the one on the system bus. | ||
+ | |||
+ | Ignore <code>/etc/init.d/dbus-scripts</code> and <code>/etc/default/dbus-scripts</code> as these would seem to have been superseded by upstart. Make a copy of <code>/etc/event.d/dbus-scripts</code> as <code>/etc/event.d/dbus-scripts-session</code> and edit it so your <code>/etc/event.d/dbus-scripts-session</code> will look like this: | ||
+ | |||
+ | <source lang="text"> | ||
+ | Nokia-N900:~# cat /etc/event.d/dbus-scripts | ||
+ | description "dbus-scripts-session" | ||
+ | author "Graham Cobb <g+770@cobb.uk.net>" | ||
+ | |||
+ | start on started hildon-desktop | ||
+ | #stop on stopping hildon-desktop | ||
+ | stop on starting shutdown | ||
+ | |||
+ | console none | ||
+ | |||
+ | pre-start script | ||
+ | /usr/bin/run-standalone.sh /usr/sbin/waitdbus session | ||
+ | end script | ||
+ | |||
+ | exec run-standalone.sh /usr/sbin/dbus-scripts --session | ||
+ | |||
+ | respawn | ||
+ | </source> | ||
+ | |||
+ | and you can then configure dbus-scripts with statements such as | ||
+ | |||
+ | <source lang="text"> | ||
+ | #:Browser launched | ||
+ | /usr/bin/onbrowser * * com.nokia.HildonDesktop.AppMgr LaunchApplication browser | ||
+ | </source> | ||
+ | |||
+ | to react to, in this example, the browser being started. | ||
+ | |||
+ | ==== The incredibly useful --debug flag ==== | ||
+ | |||
+ | The --debug flag is essential for looking at what's going on if you plan to write your own scripts. Run "dbus-scripts --debug" in a terminal and then perform whatever actions on the phone, to see all the events you can process using dbus-scripts. To see events on the session bus, make that "dbus-scripts --debug --session" (but you will need to run it through <code>run-standalone.sh</code>). For example: | ||
+ | |||
+ | <pre> | ||
+ | sudo dbus-scripts --debug | ||
+ | sudo /usr/bin/run-standalone.sh dbus-scripts --debug --session | ||
+ | </pre> | ||
+ | |||
+ | == Usage, tips and tricks == | ||
+ | |||
+ | dbus-scripts does not reread edited configuration automatically. After editing (or creating/removing) a configuration file, you need to restart it. | ||
+ | |||
+ | On N900 it can easily be done with | ||
+ | killall dbus-scripts | ||
+ | since it is then restarted by upstart. On 770/N800/N810, use | ||
+ | /etc/init.d/dbus-scripts restart | ||
+ | |||
+ | The package dbus-scripts-settings contains a script <code>logit.sh</code> which simply logs its parameters to the file <code>/media/mmc1/logit.log</code>. If you want to find out if some event generates D-Bus events, and what configuration line to use for it, create a file in <code>/etc/dbus-scripts.d/</code> containing: | ||
+ | |||
+ | /usr/bin/logit.sh * | ||
+ | |||
+ | And generate the event. Reading the log file will show you all the D-Bus activity. | ||
== Limitations == | == Limitations == | ||
- | For the moment dbus-scripts will only pass simple | + | For the moment dbus-scripts will only pass simple D-Bus type arguments like string, boolean and int32 et uint32 to your scripts. You will miss variant, array and all other complex type arguments. So if you want to play, for example, with text of received SMS, you can't yet use dbus-scripts as the text is an array of bytes. |
- | like string, boolean and int32 et uint32 to your scripts. You will | + | |
- | miss variant, array and all other complex type arguments. So if you | + | |
- | want to play, for example, with text of received | + | |
- | use dbus-scripts as the text is an array of bytes. | + | |
- | = Some scripts = | + | == Some scripts == |
- | Here are some things you can do, if you have others, add them. Those | + | Here are some things you can do, if you have others, add them. Those examples aren't rocket science and I've stolen some ideas from here and there on web. |
- | examples aren't rocket science and I've stolen some ideas from here | + | |
- | and there on web. | + | |
- | == when connect on my wifi home network register only SIP | + | === when connect on my wifi home network register only SIP and turn on noisy ring === |
- | When I come back home I want my SIP call redirected to my mobile phone and the phone ring turn on and when I leave I want my ring turn off so I wait for | + | When I come back home I want my SIP call redirected to my mobile phone and the phone ring turn on and when I leave I want my ring turn off so I wait for D-Bus event on my home wifi network and register/unregister SIP and turn on/off the ring. |
I use this dbus-scripts config file : | I use this dbus-scripts config file : | ||
- | + | '''/etc/dbus-scripts.d/athome''' | |
/home/user/bin/athome * * com.nokia.icd status_changed * WLAN_INFRA | /home/user/bin/athome * * com.nokia.icd status_changed * WLAN_INFRA | ||
and this script to register/unregister SIP and turn on/off the ring. | and this script to register/unregister SIP and turn on/off the ring. | ||
- | + | '''/home/user/bin/athome''' | |
- | + | <source lang="python"> | |
- | + | #!/usr/bin/python | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | import dbus | |
+ | import sys | ||
+ | import re | ||
+ | import telepathy | ||
- | + | wifi = '91f493fb-7c89-4fc6-ac2c-b822923dde45' | |
- | + | # /home/user/bin/w32g * * com.nokia.icd status_changed * WLAN_INFRA * | |
- | + | ||
- | + | wifi_id,state = sys.argv[5],sys.argv[7] | |
- | + | ||
- | + | ||
+ | # if we are not on the good wifi network we do nothing and exit | ||
+ | if wifi and not re.search(wifi_id, wifi, re.IGNORECASE): | ||
+ | sys.exit(0) | ||
- | + | bus = dbus.SessionBus() | |
- | + | account = bus.get_object('org.freedesktop.Telepathy.AccountManager', | |
- | + | '/org/freedesktop/Telepathy/Account/sofiasip/sip/_309517129090') | |
- | + | profile = bus.get_object('com.nokia.profiled', '/com/nokia/profiled') | |
- | + | ||
- | + | def change_state(prof, presence_const, presence_text): | |
- | + | profile.set_profile(prof, dbus_interface='com.nokia.profiled') | |
- | + | account.Set('org.freedesktop.Telepathy.Account', 'RequestedPresence', \ | |
- | + | dbus.Struct(( dbus.UInt32( presence_const) , presence_text, ""), signature='uss'), | |
- | + | dbus_interface='org.freedesktop.DBus.Properties') | |
- | + | ||
- | + | ||
- | + | if state == "CONNECTED": | |
- | + | print "available" | |
- | + | change_state('general', telepathy.constants.CONNECTION_PRESENCE_TYPE_AVAILABLE, 'available') | |
- | + | elif state == "IDLE": | |
- | + | print "offline" | |
- | + | change_state('silent', telepathy.constants.CONNECTION_PRESENCE_TYPE_OFFLINE, 'offline') | |
- | + | else: | |
- | + | sys.exit("Usage: %s (start|stop)" % sys.argv[0]) | |
- | + | </source> | |
- | + | ||
- | + | === turn to 2g when connected to wifi network === | |
- | + | ||
- | + | There's already an [[Fcron#Auto_2G_with_WiFi|alternative method with fcron]]. Here is a D-Bus method : | |
- | + | ||
- | + | '''/etc/dbus-scripts.d/w32g''' | |
- | + | /home/user/bin/w32g.py * * com.nokia.icd status_changed * WLAN_INFRA | |
- | + | ||
- | + | Installation documentation is at the beginning of the script and configuration documentation is at the beginning of the configuration file: | |
- | + | ||
- | + | '''/home/user/bin/w32g.py''' | |
- | + | <source lang="python"> | |
- | + | #!/usr/bin/python | |
- | + | ||
- | + | import sys | |
- | + | import os | |
- | + | import ConfigParser | |
- | + | import re | |
- | + | ||
- | + | # You need to install dbus-scripts (BE CAREFUL, it's a devel package) | |
- | + | # | |
- | + | # as root you need to create a file /etc/sudoers.d/w32g | |
- | + | # with this line : | |
- | + | # user ALL = NOPASSWD: /bin/ping | |
- | + | # and run the command update-sudoers because only root can use ping | |
- | + | # | |
- | + | # always as root and you need to create another file | |
- | + | # /etc/dbus-scripts/w32g with the following line : | |
- | + | # | |
- | + | # /home/user/bin/w32g * * com.nokia.icd status_changed * WLAN_INFRA * | |
- | + | # | |
- | + | # and copy this script in /home/user/bin/w32g | |
- | + | ||
- | + | conf_file = '/home/user/.w32g.conf' | |
- | + | ||
- | + | config = { 'message_on_idle': '3G cellular mode set', | |
- | + | 'message_on_connected': '2G (GSM) cellular mode set', | |
- | + | 'connection_test': 'sudo /bin/ping -c 1 www.google.com', | |
- | + | 'change_on_idle': 'true', | |
- | + | 'change_to_dual': 'true', | |
- | + | 'wifi': '' | |
- | + | } | |
- | + | ||
- | + | config_parser = ConfigParser.SafeConfigParser() | |
- | + | ||
- | + | try: | |
- | + | config_parser.read(conf_file) | |
- | + | for item in config_parser.items('w32g'): | |
- | + | config[ item[0] ] = item[1] | |
- | + | except ConfigParser.NoSectionError: | |
- | + | pass | |
- | + | ||
- | + | def to_bool(string): | |
+ | if re.search('true', string, re.IGNORECASE): | ||
+ | return True | ||
+ | |||
+ | config['change_on_idle'] = to_bool(config['change_on_idle']) | ||
+ | config['change_to_dual'] = to_bool(config['change_to_dual']) | ||
+ | |||
+ | wifi_id,state = sys.argv[5],sys.argv[7] | ||
+ | |||
+ | # if we are not on the good wifi network we do nothing and exit | ||
+ | if config['wifi'] and not re.search(wifi_id, config['wifi'], re.IGNORECASE): | ||
+ | sys.exit(0) | ||
+ | |||
+ | dbus_wifi = "dbus-send --system --type=method_call --print-reply --dest=com.nokia.phone.net /com/nokia/phone/net Phone.Net.set_selected_radio_access_technology"; | ||
+ | |||
+ | dbus_notif = "dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.Notifications /org/freedesktop/Notifications org.freedesktop.Notifications.SystemNoteInfoprint"; | ||
+ | |||
+ | if state == 'CONNECTED': | ||
+ | # we verified that the connection works | ||
+ | if config['connection_test']: | ||
+ | ret = os.system(config['connection_test']); | ||
+ | else: | ||
+ | ret = 0 | ||
+ | if ret == 0: | ||
+ | os.system(dbus_wifi + " byte:1"); | ||
+ | os.system(dbus_notif + " string:'" + config['message_on_connected'] + "'") | ||
+ | |||
+ | elif state == 'IDLE' and config['change_on_idle']: | ||
+ | dual = "2" | ||
+ | if config['change_to_dual']: dual = "0" | ||
+ | print dual | ||
+ | os.system(dbus_wifi + " byte:" + dual); | ||
+ | os.system(dbus_notif + " string:'" + config['message_on_idle'] + "'") | ||
+ | </source> | ||
+ | |||
+ | '''/home/user/.w32g.conf''' | ||
+ | <pre> | ||
+ | [w32g] | ||
+ | |||
+ | ### the comment reflect the default configuration | ||
+ | ### true is true (case insensitive), everything else is false | ||
+ | |||
+ | ### the command used to test the wifi connection before downgrading to 2g | ||
+ | ### if you don't want to do test just delete after = | ||
+ | # connection_test = sudo /bin/ping -c 1 www.google.com | ||
+ | |||
+ | ### notification message when going to 3g | ||
+ | # message_on_idle = 3G cellular mode set | ||
+ | ### notification message when going to 2g | ||
+ | # message_on_connected = 2G (GSM) cellular mode set | ||
+ | |||
+ | ### shall we go back to 3g when wlan disconnect ? | ||
+ | #### if you want to have false just delete after = | ||
+ | # change_on_idle = true | ||
+ | ### shall we go back to dual or 3g ? | ||
+ | # change_to_dual = true | ||
+ | |||
+ | ### if you want go to 2g only when connected to some wlan you can add their wlan id here | ||
+ | ### you can find the wlan id using the command | ||
+ | ### gconftool-2 -R /system/osso/connectivity/IAP | ||
+ | ### For example : | ||
+ | ### wifi = 91f493fb-7c89-4fc6-ac2c-b822923dde45 9ee5dd55-9a32-4ee9-9131-c464ad31d907 | ||
+ | # wifi = | ||
+ | </pre> | ||
+ | |||
+ | === Enable tethering when connecting to Bluetooth PAN === | ||
+ | |||
+ | Tethering is a bit of a hassle in Maemo. Most people refer to JoikuSpot, which is definitely the most comfortable solution, but it's not free. So, why not use builtin (or easily installable) functionality to use the N900 as a modem? | ||
+ | |||
+ | For this script to work, you need to have at least bluetooth-dun and iptables installed. Possibly other packages too, please test and correct. | ||
+ | |||
+ | '''/etc/dbus-scripts.d/tethering''' | ||
+ | /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceAdded | ||
+ | /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceRemoved | ||
+ | |||
+ | '''/usr/bin/tethering.sh''' | ||
+ | <source lang="bash"> | ||
+ | #!/bin/sh | ||
+ | # | ||
+ | # /usr/bin/tethering.sh | ||
+ | # | ||
+ | # Enable tethering on USB network and Bluetooth PAN. | ||
+ | # | ||
+ | # Note that the INETDEV must be up and running when this script is | ||
+ | # started, or forwarding will not be enabled. | ||
+ | # Having tethering on multiple devices concurrently is possible, | ||
+ | # but forwarding will be disabled once the first device disconnetcs. | ||
+ | # A better check is necessary. | ||
+ | # | ||
+ | # Put this into /etc/dbus-scripts/tethering : | ||
+ | # /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceAdded | ||
+ | # /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceRemoved | ||
+ | # | ||
+ | # dbus-scripts doesn't give us the event path, so we have to use a more | ||
+ | # convoluted method of getting the network interface. | ||
+ | # | ||
+ | |||
+ | EVENT="$4" | ||
+ | UDI="$5" | ||
+ | HALDEV="$(echo $UDI | sed 's#.*/\([0-9a-zA-Z_]*\)#\1#')" | ||
+ | LOG="/tmp/tethering.log" | ||
+ | INETDEV="gprs0" | ||
+ | RUNFILE="/var/run/tethering.$HALDEV.pid" | ||
+ | IFACEFILE="/var/run/tethering.$HALDEV.iface" | ||
+ | |||
+ | if [ "$EVENT" = "DeviceAdded" ]; then | ||
+ | IFACE=$(hal-get-property --udi $UDI --key net.interface) | ||
+ | case $IFACE in | ||
+ | bnep0 ) | ||
+ | IF_ADDRESS=192.168.254.254 | ||
+ | IF_RANGE=192.168.254.1,192.168.254.254 | ||
+ | ;; | ||
+ | usb0 ) | ||
+ | IF_ADDRESS=192.168.253.254 | ||
+ | IF_RANGE=192.168.253.1,192.168.253.254 | ||
+ | ;; | ||
+ | * ) | ||
+ | exit 0 | ||
+ | ;; | ||
+ | esac | ||
+ | |||
+ | echo "$(date) Enabling tethering on interface $IFACE" >> $LOG | ||
+ | echo "$IFACE" > $IFACEFILE | ||
+ | ifconfig "$IFACE" "$IF_ADDRESS" | ||
+ | /sbin/modprobe ipt_MASQUERADE | ||
+ | /usr/sbin/iptables -t nat -A POSTROUTING -o $INETDEV -j MASQUERADE | ||
+ | start-stop-daemon -S -p "$RUNFILE" -m -b -x /usr/sbin/dnsmasq -- -k -I lo -i "$IFACE" -a $IF_ADDRESS -z -F $IF_RANGE,3600 | ||
+ | echo 1 > /proc/sys/net/ipv4/conf/$IFACE/forwarding | ||
+ | echo 1 > /proc/sys/net/ipv4/conf/$INETDEV/forwarding | ||
+ | fi | ||
+ | |||
+ | if [ "$EVENT" = "DeviceRemoved" ]; then | ||
+ | if [ ! -f "$IFACEFILE" ]; then | ||
+ | exit 0 | ||
+ | fi | ||
+ | IFACE="$(cat $IFACEFILE)" | ||
+ | rm "$IFACEFILE" | ||
+ | echo "$(date) Disabling tethering on device $IFACE" >> $LOG | ||
+ | echo 0 > /proc/sys/net/ipv4/conf/$INETDEV/forwarding | ||
+ | start-stop-daemon -K -p "$RUNFILE" -x /usr/sbin/dnsmasq | ||
+ | /usr/sbin/iptables -t nat -D POSTROUTING -o $INETDEV -j MASQUERADE | ||
+ | fi | ||
+ | |||
+ | exit 0 | ||
+ | </source> | ||
+ | |||
+ | === Set MSN (pecan) to stay offline when disconnected === | ||
+ | |||
+ | Since MSN allows only one client connection at a time, signing into MSN from the N900 disconnects it on my desktop PC, and vice versa. | ||
+ | |||
+ | Pidgin on my desktop PC handles this gracefully by disabling the account until I manually connect it. msn-pecan, on the other hand, helpfully auto-reconnects on its own authority and fails to respect my disabling auto-connect via mc-tool; if the requested status remains as "available", it reconnects, and that disconnects Pidgin on my desktop again. Grr. | ||
+ | |||
+ | You need an instance of dbus-scripts '''on the session bus''' for this one (see above), then you can do the following: | ||
+ | |||
+ | ==== /etc/dbus-scripts.d/dbus-scripts-settings ==== | ||
+ | <source lang="text"> | ||
+ | #:MSN (pecan) disconnected (session bus) | ||
+ | /usr/bin/offmsn * * org.freedesktop.DBus ReleaseName org.freedesktop.Telepathy.Connection.pecan.msn_pecan.* | ||
+ | </source> | ||
+ | ==== /usr/bin/offmsn ==== | ||
+ | |||
+ | <source lang="bash"> | ||
+ | #!/bin/sh | ||
+ | # If we got disconnected from MSN (eg by signing in via Pidgin on desktop PC) then set it offline until further notice | ||
+ | mc-tool list | grep "pecan/msn" | awk {'print "mc-tool request "$1" offline"'} | sh | ||
+ | </source> | ||
+ | |||
+ | === Adjust Google Voice settings based on network location === | ||
+ | |||
+ | I use this script to enable and disable various phones so that my office phone doesn't auto-answer when I'm not there, and my home phone doesn't bother my family when I'm at the office. | ||
+ | |||
+ | To run this script, you will need to install [http://code.google.com/p/pygooglevoice/ pygooglevoice]. Depending on the version of pygooglevoice you have, you may also have to change LOGIN in /usr/local/lib/python2.5/site-packages/googlevoice/settings.py to read: | ||
+ | <source lang="text"> | ||
+ | LOGIN = 'https://accounts.google.com/ServiceLogin?service=grandcentral&passive=1209600&continue=https://www.google.com/voice&followup=https://www.google.com/voice<mpl=open' | ||
+ | </source> | ||
+ | as per [http://code.google.com/p/pygooglevoice/issues/detail?id=58#c24 this bug report]. | ||
+ | |||
+ | Then stick this script in /home/user/bin/gvlocation and make it executable: | ||
+ | |||
+ | <source lang="python"> | ||
+ | #!/usr/bin/python | ||
+ | |||
+ | import ConfigParser | ||
+ | import dbus | ||
+ | from googlevoice import Voice | ||
+ | from googlevoice.util import LoginError | ||
+ | import os | ||
+ | import re | ||
+ | import signal | ||
+ | import sys | ||
+ | import time | ||
+ | import traceback | ||
+ | from urllib2 import URLError | ||
+ | |||
+ | # You need to install dbus-scripts (BE CAREFUL, it's a devel package) | ||
+ | # | ||
+ | # Update Google Voice settings based on location changes. | ||
+ | # | ||
+ | |||
+ | conf_files = ('/home/user/.gvlocation.conf',) | ||
+ | |||
+ | # Default configuration | ||
+ | config = \ | ||
+ | { | ||
+ | 'auth': | ||
+ | { | ||
+ | 'google_id': None, | ||
+ | 'google_pw': None, | ||
+ | }, | ||
+ | 'configuration': | ||
+ | { | ||
+ | # Turn on to log activity | ||
+ | 'debug': True, | ||
+ | # Where to log activity | ||
+ | 'log_file': '/home/user/MyDocs/gvl.log', | ||
+ | # Time limit for giving up on login attempts | ||
+ | 'login_time_limit': 60, | ||
+ | # Time limit for giving up on individual operations | ||
+ | 'operation_time_limit': 30, | ||
+ | # Maximum number of login attempts | ||
+ | 'login_limit': 5, | ||
+ | # Sleep time between login attempts | ||
+ | 'login_sleep': 10, | ||
+ | # Whether to use multi-line (persistent) messages for notifications | ||
+ | 'persistent_notify': True, | ||
+ | |||
+ | # Sections that describe phone numbers (space-separated) | ||
+ | 'phones': '', | ||
+ | # Sections that describe networks (space-separated) | ||
+ | 'networks': '', | ||
+ | }, | ||
+ | } | ||
+ | |||
+ | class Alarm(object): | ||
+ | """ | ||
+ | Support alarms. For simplicity, only one alarm is allowed at a time. | ||
+ | """ | ||
+ | def __init__(self, timeout, purpose): | ||
+ | """ | ||
+ | Create an alarm with a given (floating-point) timeout. | ||
+ | For convenience, the timeout can be given as a string. | ||
+ | The purpose is a string used in messages if the alarm expires. | ||
+ | """ | ||
+ | Alarm.__purpose = purpose | ||
+ | signal.signal(signal.SIGALRM, Alarm.alarm_handler) | ||
+ | # Python 2 doesn't have setitimer | ||
+ | #signal.setitimer(signal.ITIMER_REAL, Alarm.__timeout) | ||
+ | #Alarm.__timeout = float(timeout) | ||
+ | Alarm.__timeout = int(timeout) | ||
+ | signal.alarm(Alarm.__timeout) | ||
+ | |||
+ | class AlarmError(Exception): | ||
+ | def __init__(self, msg): | ||
+ | super(Alarm.AlarmError, self).__init__(msg) | ||
+ | |||
+ | @staticmethod | ||
+ | def alarm_handler(signum, frame): | ||
+ | log('%d-second alarm received: %s\n' | ||
+ | % (Alarm.__timeout, Alarm.__purpose)) | ||
+ | raise Alarm.AlarmError(Alarm.__purpose) | ||
+ | |||
+ | def cancel(self): | ||
+ | """Cancel an alarm.""" | ||
+ | # Python 2 doesn't have setitimer | ||
+ | #signal.setitimer(signal.ITIMER_REAL, 0) | ||
+ | signal.alarm(0) | ||
+ | |||
+ | def main(): | ||
+ | log('%s\n%s\n' % (time.ctime(), sys.argv)) | ||
+ | |||
+ | parse_config_file() | ||
+ | |||
+ | # | ||
+ | # Pick up which network we're in. We care only about the ID and state. | ||
+ | # | ||
+ | network_id, network_type, state = sys.argv[5:8] | ||
+ | |||
+ | # | ||
+ | # We only take action when we get connected to a network. | ||
+ | # | ||
+ | if state != 'CONNECTED': | ||
+ | sys.exit(0) | ||
+ | |||
+ | # | ||
+ | # Log in to the Google Voice account. | ||
+ | # | ||
+ | voice = login_to_voice() | ||
+ | if voice is None: | ||
+ | sys.exit(1) | ||
+ | |||
+ | # | ||
+ | # Handle connection to the network. | ||
+ | # | ||
+ | handle_connection(network_id, voice) | ||
+ | |||
+ | def parse_config_file(): | ||
+ | """ | ||
+ | Parse the configuration file(s) and convert them to a global dictionary. | ||
+ | """ | ||
+ | global config | ||
+ | parser = ConfigParser.SafeConfigParser() | ||
+ | parser.read(conf_files) | ||
+ | for section in parser.sections(): | ||
+ | if section not in config: | ||
+ | config[section] = {} | ||
+ | for (name, value) in parser.items(section): | ||
+ | config[section][name] = value | ||
+ | # | ||
+ | # Ensure that some basic stuff is given | ||
+ | # | ||
+ | for var in ('google_id', 'google_pw'): | ||
+ | if config['auth'][var] is None or config['auth'][var] == '': | ||
+ | log('Missing required authorization variable %s\n' % var, | ||
+ | True, True) | ||
+ | # | ||
+ | # Convert the lists of phones and networks. For phones, we | ||
+ | # insist on commma as a separator because people often use | ||
+ | # blanks as a readability character. | ||
+ | # | ||
+ | config['configuration']['phones'] = \ | ||
+ | [p.strip() for p in config['configuration']['phones'].split(',') \ | ||
+ | if p.strip() != ''] | ||
+ | config['configuration']['networks'] = \ | ||
+ | [n for n in re.split('[, ]', config['configuration']['networks']) \ | ||
+ | if n != ''] | ||
+ | # | ||
+ | # Make sure the phone and network sections have a full set of | ||
+ | # keys; this simplifies the rest of the code. | ||
+ | # | ||
+ | for phone in [config[p] for p in config['configuration']['phones']]: | ||
+ | for phone_key in ('number', 'enable_networks', 'disable_networks', | ||
+ | 'enable_message', 'disable_message'): | ||
+ | if phone_key not in phone: | ||
+ | phone[phone_key] = '' | ||
+ | if phone['enable_message'] == '': | ||
+ | phone['enable_message'] = phone['number'] + ' enabled.' | ||
+ | if phone['disable_message'] == '': | ||
+ | phone['disable_message'] = phone['number'] + ' disabled.' | ||
+ | for network in config['configuration']['networks']: | ||
+ | for network_key in ('network_id', 'enable_phones', 'disable_phones', | ||
+ | 'active_message'): | ||
+ | if network_key not in config[network]: | ||
+ | config[network][network_key] = '' | ||
+ | if config[network]['active_message'] == '': | ||
+ | config[network]['active_message'] = network + ' activated.' | ||
+ | |||
+ | def login_to_voice(): | ||
+ | """ | ||
+ | Login to Google Voice, returning a Voice object or None if failure. | ||
+ | """ | ||
+ | voice = Voice() | ||
+ | # | ||
+ | # GPRS connections sometimes get reported before they're | ||
+ | # actually ready. So we'll try more than once. But we won't | ||
+ | # go forever, because we don't want to totally screw thing up | ||
+ | # in flaky-connectivity cases. | ||
+ | # | ||
+ | logged_in = False | ||
+ | for trial in range(int(config['configuration']['login_limit'])): | ||
+ | try: | ||
+ | alarm = Alarm(config['configuration']['login_time_limit'], | ||
+ | 'logging in') | ||
+ | voice.login(config['auth']['google_id'], | ||
+ | config['auth']['google_pw']) | ||
+ | alarm.cancel() | ||
+ | log('Logged in on trial #%d\n' % (trial + 1)) | ||
+ | logged_in = True | ||
+ | break | ||
+ | except LoginError, msg: | ||
+ | alarm.cancel() | ||
+ | log("Voice login failure due to LoginError, retrying: %s\n" % msg) | ||
+ | # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" | ||
+ | except Alarm.AlarmError, msg: | ||
+ | log("Voice login failure due to timeout, retrying: %s\n" % msg) | ||
+ | # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" | ||
+ | except URLError, msg: | ||
+ | alarm.cancel() | ||
+ | log("Voice login failure due to URLError, retrying: %s\n" % msg) | ||
+ | # Voice gets an AttributeError when an re search fails; this | ||
+ | # happens if the HTTP fetch returns incorrect or partial data. | ||
+ | # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" | ||
+ | except AttributeError, msg: | ||
+ | alarm.cancel() | ||
+ | log("Voice login failure due to AttributeError, retrying: %s\n" | ||
+ | % msg) | ||
+ | # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" | ||
+ | except Exception, msg: | ||
+ | info = sys.exc_info() | ||
+ | alarm.cancel() | ||
+ | log("Surprise exception\n") | ||
+ | log(''.join(traceback.format_exception(*info))) | ||
+ | except: | ||
+ | info = sys.exc_info() | ||
+ | alarm.cancel() | ||
+ | log("Random exception\n") | ||
+ | log(''.join(traceback.format_exception(*info))) | ||
+ | time.sleep(int(config['configuration']['login_sleep'])) | ||
+ | |||
+ | if not logged_in: | ||
+ | log("login failed\n") | ||
+ | show_notification_dialog("Couldn't log in to Google Voice") | ||
+ | return None | ||
+ | |||
+ | return voice | ||
+ | |||
+ | log_file = None | ||
+ | |||
+ | def log(string, flush = True, copy_to_stderr = False): | ||
+ | """ | ||
+ | Write a string to the configured log file, opening it if necessary. | ||
+ | Errors on open are silently suppressed. If 'flush' is true, flush | ||
+ | the log information immediately. | ||
+ | """ | ||
+ | global log_file | ||
+ | if copy_to_stderr or sys.stderr.isatty(): | ||
+ | sys.stderr.write(string) | ||
+ | if not to_boolean(config['configuration']['debug']): | ||
+ | return | ||
+ | if log_file is None: | ||
+ | # | ||
+ | # Don't die on open failures | ||
+ | # | ||
+ | try: | ||
+ | log_file = open(config['configuration']['log_file'], 'a') | ||
+ | if not sys.stderr.isatty(): | ||
+ | sys.stderr.close() | ||
+ | sys.stderr = log_file | ||
+ | except: | ||
+ | log_file = None | ||
+ | config['configuration']['debug'] = False | ||
+ | log_file.write(string) | ||
+ | if flush: | ||
+ | log_file.flush() | ||
+ | |||
+ | def to_boolean(value): | ||
+ | if isinstance(value, (int, bool)): | ||
+ | return value | ||
+ | value = value.lower() | ||
+ | if value in ('1', 'yes', 'true', 'on'): | ||
+ | return True | ||
+ | elif value in ('0', 'no', 'false', 'off'): | ||
+ | return False | ||
+ | else: | ||
+ | raise ValueError | ||
+ | |||
+ | def handle_connection(network, voice): | ||
+ | """ | ||
+ | Deal with becoming connected to 'network' by manipulating the | ||
+ | Google Voice account named by 'voice'. The exact actions to take | ||
+ | are given by the phone and network configuration parameters. | ||
+ | """ | ||
+ | (enable_phone_numbers, disable_phone_numbers, phone_message) = \ | ||
+ | handle_phones(network, voice) | ||
+ | (enable_network_numbers, disable_network_numbers, network_message) = \ | ||
+ | handle_networks(network, voice) | ||
+ | |||
+ | enable_numbers = set(enable_phone_numbers + enable_network_numbers) | ||
+ | disable_numbers = set(disable_phone_numbers + disable_network_numbers) | ||
+ | |||
+ | enable_disable_numbers(network, voice, enable_numbers, disable_numbers) | ||
+ | |||
+ | show_notification_dialog(add_message(phone_message, network_message)) | ||
+ | |||
+ | def enable_disable_numbers(network, voice, enable_numbers, disable_numbers): | ||
+ | """ | ||
+ | Enable and disable Google Voice numbers given in the respective lists. | ||
+ | """ | ||
+ | try: | ||
+ | alarm = Alarm(config['configuration']['operation_time_limit'], | ||
+ | 'getting phone list failed') | ||
+ | phones = voice.phones | ||
+ | alarm.cancel() | ||
+ | except Alarm.AlarmError, msg: | ||
+ | log("failed to get Google phone list\n") | ||
+ | show_notification_dialog("Couldn't get Google phone list") | ||
+ | sys.exit(1) | ||
+ | enable_phones = [x for x in phones for y in enable_numbers \ | ||
+ | if y in x.phoneNumber] | ||
+ | disable_phones = [x for x in phones for y in disable_numbers \ | ||
+ | if y in x.phoneNumber] | ||
+ | log('en: %s\ndis: %s\n' % (enable_phones, disable_phones)) | ||
+ | for phone in enable_phones: | ||
+ | log('enabling %s\n' % phone.phoneNumber) | ||
+ | try: | ||
+ | alarm = Alarm(config['configuration']['operation_time_limit'], | ||
+ | 'enabling phones') | ||
+ | phone.enable() | ||
+ | alarm.cancel() | ||
+ | except Alarm.AlarmError, msg: | ||
+ | log("failed to enable %s\n" % phone.phoneNumber) | ||
+ | for phone in disable_phones: | ||
+ | log('disabling %s\n' % phone.phoneNumber) | ||
+ | try: | ||
+ | alarm = Alarm(config['configuration']['operation_time_limit'], | ||
+ | 'disabling phones') | ||
+ | phone.disable() | ||
+ | alarm.cancel() | ||
+ | except Alarm.AlarmError, msg: | ||
+ | log("failed to disable %s: %s\n" % (phone.phoneNumber, msg)) | ||
+ | |||
+ | def add_message(original, append): | ||
+ | if original == '': | ||
+ | return append | ||
+ | elif append == '': | ||
+ | return original | ||
+ | if to_boolean(config['configuration']['persistent_notify']): | ||
+ | # | ||
+ | # Multiline message. | ||
+ | # | ||
+ | return original + '\n' + append | ||
+ | else: | ||
+ | # | ||
+ | # Single-line message. | ||
+ | # | ||
+ | return original + ' ' + append | ||
+ | |||
+ | def handle_phones(network, voice): | ||
+ | """ | ||
+ | Walk through the phone list, finding entries that ask for action when | ||
+ | 'network' is connected. Return a triple: a list of numbers to enable, | ||
+ | a list to disable, and a message to display. | ||
+ | """ | ||
+ | enable = [] | ||
+ | disable = [] | ||
+ | message = '' | ||
+ | for phone in [config[p] for p in config['configuration']['phones']]: | ||
+ | action = None | ||
+ | if network in phone['enable_networks'] \ | ||
+ | or phone['enable_networks'] == 'all': | ||
+ | action = 'enable' | ||
+ | elif network in phone['disable_networks'] \ | ||
+ | or phone['disable_networks'] == 'all': | ||
+ | action = 'disable' | ||
+ | elif 'other' in phone['enable_networks']: | ||
+ | action = 'enable' | ||
+ | elif 'other' in phone['disable_networks']: | ||
+ | action = 'disable' | ||
+ | |||
+ | if action == 'enable': | ||
+ | enable.append(clean_phone(phone['number'])) | ||
+ | message = add_message(message, phone['enable_message']) | ||
+ | elif action == 'disable': | ||
+ | disable.append(clean_phone(phone['number'])) | ||
+ | message = add_message(message, phone['disable_message']) | ||
+ | return (enable, disable, message) | ||
+ | |||
+ | def handle_networks(network, voice): | ||
+ | """ | ||
+ | Walk through the network list, finding entries that ask for action when | ||
+ | 'network' is connected. Return a triple: a list of numbers to enable, | ||
+ | a list to disable, and a message to display. | ||
+ | """ | ||
+ | action = None | ||
+ | enable = [] | ||
+ | disable = [] | ||
+ | message = '' | ||
+ | found_net = False | ||
+ | |||
+ | for net in [config[n] for n in config['configuration']['networks']]: | ||
+ | if network not in net['network_id'] \ | ||
+ | and net['network_id'] != 'all': | ||
+ | continue | ||
+ | found_net = True | ||
+ | message = add_message(message, net['active_message']) | ||
+ | phones = [p.strip() for p in net['enable_phones'].split(',')] | ||
+ | while '' in phones: | ||
+ | phones.remove('') | ||
+ | for phone in phones: | ||
+ | if phone in config: | ||
+ | for num in \ | ||
+ | [p.strip() for p in config[phone]['number'].split(',')]: | ||
+ | enable.append(clean_phone(num)) | ||
+ | else: | ||
+ | enable.append(clean_phone(phone)) | ||
+ | phones = [p.strip() for p in net['disable_phones'].split(',')] | ||
+ | while '' in phones: | ||
+ | phones.remove('') | ||
+ | for phone in phones: | ||
+ | if phone in config: | ||
+ | for num in \ | ||
+ | [p.strip() for p in config[phone]['number'].split(',')]: | ||
+ | disable.append(clean_phone(num)) | ||
+ | else: | ||
+ | disable.append(clean_phone(phone)) | ||
+ | |||
+ | if not found_net: | ||
+ | for net in [config[n] for n in config['configuration']['networks']]: | ||
+ | if 'other' not in net['network_id']: | ||
+ | continue | ||
+ | message = add_message(message, net['active_message']) | ||
+ | phones = [p.strip() for p in net['enable_phones'].split(',')] | ||
+ | while '' in phones: | ||
+ | phones.remove('') | ||
+ | for phone in phones: | ||
+ | if phone in config: | ||
+ | for num in \ | ||
+ | [p.strip() for p in config[phone]['number'].split(',')]: | ||
+ | enable.append(clean_phone(num)) | ||
+ | else: | ||
+ | enable.append(clean_phone(phone)) | ||
+ | phones = [p.strip() for p in net['disable_phones'].split(',')] | ||
+ | while '' in phones: | ||
+ | phones.remove('') | ||
+ | for phone in phones: | ||
+ | if phone in config: | ||
+ | for num in \ | ||
+ | [p.strip() for p in config[phone]['number'].split(',')]: | ||
+ | disable.append(clean_phone(num)) | ||
+ | else: | ||
+ | disable.append(clean_phone(phone)) | ||
+ | |||
+ | return (enable, disable, message) | ||
+ | |||
+ | def clean_phone(number): | ||
+ | """ | ||
+ | Clean a phone number by removing readability characters. | ||
+ | """ | ||
+ | return re.sub(r'[-\(\) \.]', '', number) | ||
+ | |||
+ | def show_notification_dialog(message): | ||
+ | bus = dbus.SystemBus() | ||
+ | iface = dbus.Interface(bus.get_object('org.freedesktop.Notifications', | ||
+ | '/org/freedesktop/Notifications'), | ||
+ | 'org.freedesktop.Notifications') | ||
+ | if to_boolean(config['configuration']['persistent_notify']): | ||
+ | iface.SystemNoteDialog(str(message), dbus.UInt32(0), 'Ok') | ||
+ | else: | ||
+ | iface.SystemNoteInfoprint(message) | ||
+ | |||
+ | if __name__ == "__main__": | ||
+ | main() | ||
+ | </source> | ||
+ | |||
+ | After installing the script, create /etc/dbus-scripts.d/gvlocation: | ||
+ | |||
+ | <source lang="text"> | ||
+ | /home/user/bin/gvlocation * * com.nokia.icd status_changed * * CONNECTED | ||
+ | </source> | ||
+ | |||
+ | Finally, install the following configuration file in /home/user/.gvlocation.conf and edit it to add networks and phone numbers as necessary. The simplest change is just to add your office, home, and cellphone numbers, and to add your office and home WiFi network IDs. Much more complex configurations are possible if you need them. | ||
+ | |||
+ | <source lang="text"> | ||
+ | ###################################################################### | ||
+ | ## CONFIGURATION FILE FOR GVLOCATION | ||
+ | ## | ||
+ | ## General layout: Sections are introduced by [name]. Within a section, | ||
+ | ## the variable = value notation is used. | ||
+ | ## | ||
+ | ## In all cases, the default value of a variable is given as a | ||
+ | ## commented-out line. | ||
+ | ## | ||
+ | ## There are two required sections: auth and configuration. All other | ||
+ | ## sections are user-defined; see the "phones" and "networks" variables | ||
+ | ## in the configuration section. | ||
+ | ## | ||
+ | ###################################################################### | ||
+ | |||
+ | |||
+ | ###################################################################### | ||
+ | ## AUTHORIZATION PARAMETERS | ||
+ | ###################################################################### | ||
+ | [auth] | ||
+ | |||
+ | ## Login parameters for Google Voice. These are required, and there is | ||
+ | ## no default. | ||
+ | ## | ||
+ | ## Examples: | ||
+ | ## google_id = user@example.com | ||
+ | ## google_pw = password | ||
+ | google_id = user@example.com | ||
+ | google_pw = supersecret | ||
+ | |||
+ | |||
+ | ###################################################################### | ||
+ | ## CONFIGURATION PARAMETERS | ||
+ | ###################################################################### | ||
+ | [configuration] | ||
+ | |||
+ | ## Debug control. Turn on debugging to log progress messages to a log | ||
+ | ## file. This can help if you are having trouble getting the script | ||
+ | ## to work. | ||
+ | # debug = False | ||
+ | |||
+ | ## Log file. Debugging messges go here. This file grows (slowly) | ||
+ | ## without limit, so you should occasionally remove or truncate it if | ||
+ | ## you leave debugging on. | ||
+ | #log_file = /home/user/MyDocs/gvl.log | ||
+ | |||
+ | ## Time limit for giving up on login attempts. Gvlocation tries several | ||
+ | ## times to log in to Google Voice. Each attempt is aborted if it | ||
+ | ## hasn't worked after this number of seconds. | ||
+ | #login_time_limit = 60 | ||
+ | |||
+ | ## Maximum number of login attempts. After this many tries, gvlocation | ||
+ | ## just gives up and won't change your Voice settings. | ||
+ | #login_limit = 5 | ||
+ | |||
+ | ## Sleep time between login attempts. If a login fails, gvlocation will | ||
+ | ## wait this long before trying again. That gives the phone a chance | ||
+ | ## to recover the network connection. | ||
+ | #login_sleep = 10 | ||
+ | |||
+ | ## Time limit for giving up on individual operations. This prevents | ||
+ | ## gvlocation from getting hung up after it has logged in, for example | ||
+ | ## if your phone goes out of range. | ||
+ | #operation_time_limit = 30 | ||
+ | |||
+ | ## Whether to use multi-line (persistent) messages for notifications. If | ||
+ | ## true, notification messages will stay until you acknowledge them. If | ||
+ | ## false, they will be temporary. | ||
+ | #persistent_notify = True | ||
+ | |||
+ | ######################################## | ||
+ | ## The two parameters below are the heart of your configuration. | ||
+ | ## There are two ways to organize your setup: by phone, or by | ||
+ | ## network. You can even intermix them, though I don't recommend it. | ||
+ | ## I prefer by-network organization. | ||
+ | ## | ||
+ | ## In a by-network setup, you basically say "when I'm on network X, | ||
+ | ## enable or disable the following phones." So when you're on your | ||
+ | ## home network, you'd enable your home phone and disable the office | ||
+ | ## phone; at the office it would be vice versa. | ||
+ | ## | ||
+ | ## In a by-phone setup, you organize things by phones. Your office | ||
+ | ## phone might say "enable me when I'm network A, disable me on B." | ||
+ | ## It's really just a different way of laying things out. | ||
+ | ## | ||
+ | ## List of phones. The names give are user-chosen mnemonics that | ||
+ | ## match sections below. The names "auth" and "configuration" are | ||
+ | ## prohibited. Separate by blanks or commas. | ||
+ | ## | ||
+ | ## Example: | ||
+ | ## phones = samplephone, homephone, officephone | ||
+ | ## | ||
+ | #phones= | ||
+ | |||
+ | ## List of networks. Again, the names are user-chosen separated by | ||
+ | ## blanks or commas. | ||
+ | ## | ||
+ | ## Example: | ||
+ | ## networks = homenet, officenet, othernet | ||
+ | ## | ||
+ | #networks= | ||
+ | |||
+ | networks = homenet, officenet, othernet | ||
+ | |||
+ | |||
+ | |||
+ | ###################################################################### | ||
+ | ## SAMPLE PHONE DEFINITION | ||
+ | ###################################################################### | ||
+ | ## | ||
+ | ## If the phone is in the "phones" list, then every time the | ||
+ | ## network changes, gvlocation will consider enabling or disabling it. | ||
+ | ## | ||
+ | [samplephone] | ||
+ | ## Phone number(s) associated with this phone. Readability characters | ||
+ | ## (parentheses, blanks, hypens, and periods) are ignored. | ||
+ | ## | ||
+ | #number= | ||
+ | number = 818-555-1212, (213) 555-1212 | ||
+ | |||
+ | ## Network IDs on which the phone(s) should be enabled. Separate multiple | ||
+ | ## networks by blanks. "all" matches all networks and must appear | ||
+ | ## alone. "other" matches all networks that aren't matched by another | ||
+ | ## pattern. | ||
+ | ## | ||
+ | ## You can find the network id using the command | ||
+ | ## gconftool-2 -R /system/osso/connectivity/IAP | ||
+ | ## or, more simply, by just turning on debugging and examining the log file. | ||
+ | ## | ||
+ | #enable_networks = | ||
+ | enable_networks = 91f493fb-7c89-4fc6-ac2c-b822923dde45 9ee5dd55-9a32-4ee9-9131-c464ad31d907 | ||
+ | |||
+ | ## Network IDs on which the phone(s) should be disabled. Separate multiple | ||
+ | ## networks by blanks. "All" matches all networks. "Other" matches all | ||
+ | ## networks that aren't matched by another pattern. | ||
+ | ## | ||
+ | #disable_networks= | ||
+ | disable_networks = other | ||
+ | |||
+ | ## Text of message to display when this phone is enabled. (gvlocation may | ||
+ | ## chose to combine this with other messages.) | ||
+ | #enable_message = 818-555-1212, (213) 555-1212 enabled. | ||
+ | |||
+ | ## Text of message to display when this phone is disabled. (gvlocation may | ||
+ | ## chose to combine this with other messages.) | ||
+ | #disable_message = 818-555-1212, (213) 555-1212 disabled. | ||
+ | |||
+ | ###################################################################### | ||
+ | ## SAMPLE PHONE-NUMBER DEFINITION. | ||
+ | ###################################################################### | ||
+ | ## | ||
+ | ## A phone can be defined even if it's not in the "phones" list; this | ||
+ | ## lets you use a symbolic name instead of a phone number. | ||
+ | ## | ||
+ | [homephone] | ||
+ | ## Phone number(s) associated with this "phone". See above. | ||
+ | #number = | ||
+ | number = 213-555-1212 | ||
+ | |||
+ | ### Office phone | ||
+ | [officephone] | ||
+ | number = 310-555-1212 | ||
+ | |||
+ | ### Cellphone | ||
+ | [cellphone] | ||
+ | number = 212-555-1212 | ||
+ | |||
+ | |||
+ | ###################################################################### | ||
+ | ## HOME NETWORK DEFINITION | ||
+ | ###################################################################### | ||
+ | [homenet] | ||
+ | ## Network ID. You can find the network id using the command | ||
+ | ## gconftool-2 -R /system/osso/connectivity/IAP | ||
+ | ## or by turning on debugging and examining the log file. | ||
+ | ## | ||
+ | ## Multiple networks can be specified by separating the IDs with blanks. | ||
+ | ## "all" applies to all networks; "other" applies to any network not matched | ||
+ | ## by another network section. | ||
+ | network_id = 740ca5b8-8c0f-496e-b8cc-a458680a30a4 | ||
+ | |||
+ | ## Phone(s) to enable when on this network. The numbers can be | ||
+ | ## digit strings (as above) or references to configuration sections; in | ||
+ | ## the latter case the "number" option from that section is used. | ||
+ | ## separate numbers by blanks. | ||
+ | ## | ||
+ | ## Example: | ||
+ | ## | ||
+ | ## enable_phones = 800-555-1212, homephone | ||
+ | ## | ||
+ | #enable_phones = | ||
+ | enable_phones = homephone | ||
+ | |||
+ | ## Phone(s) to disable when on this network. See enable_phones. | ||
+ | #disable_phones = | ||
+ | disable_phones = officephone | ||
+ | |||
+ | ## Message to display when this network activates. | ||
+ | #active_message = 740ca5b8-8c0f-496e-b8cc-a458680a30a4 activated. | ||
+ | active_message = Home phone enabled, office phone disabled. | ||
+ | |||
+ | |||
+ | ### Office network | ||
+ | [officenet] | ||
+ | ## Network ID. | ||
+ | network_id = b501d585-ded3-4c7e-88db-153879e1cea0 | ||
+ | enable_phones = officephone | ||
+ | disable_phones = homephone | ||
+ | #active_message = b501d585-ded3-4c7e-88db-153879e1cea0 activated. | ||
+ | active_message = Office phone enabled, home phone disabled. | ||
+ | |||
+ | |||
+ | ### Other networks | ||
+ | [othernet] | ||
+ | ## Network ID. This will match any network not matched by another section | ||
+ | ## listed in "networks". | ||
+ | network_id = other | ||
+ | |||
+ | ## Phone number(s) to enable when on this network. | ||
+ | #enable_phones = | ||
+ | ## Phone number(s) to disable when on this network. | ||
+ | #disable_phones = | ||
+ | disable_phones = homephone, officephone | ||
- | + | ## Message to display when this network activates. | |
- | + | #active_message = other activated. | |
- | + | active_message = Home and office phones disabled. | |
- | + | </source> | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | = Links to other scripts = | + | == Links to other scripts == |
- | == automatically register on public wifi network when connected == | + | === automatically register on public wifi network when connected === |
You can find a script [http://talk.maemo.org/showpost.php?p=555757&postcount=6 in this post on tmo] | You can find a script [http://talk.maemo.org/showpost.php?p=555757&postcount=6 in this post on tmo] | ||
- | = Thanks = | + | == Thanks == |
Thanks to Graham Cobb for dbus-scripts and Matan for dbus-scripts-settings | Thanks to Graham Cobb for dbus-scripts and Matan for dbus-scripts-settings |
Latest revision as of 08:02, 31 October 2012
dbus-scripts is a daemon that can execute a command when various actions occurs on D-Bus. Some D-Bus signals you can watch for are :
- keyboard slide
- battery full, low or empty
- connection or disconnection from a network
- sms or phone call arriving
- desktop started (ie. after boot is completed)
- bnep or usb network started or stopped (use to configure or run firewalls etc)
You can find dbus-scripts in extras-devel (so be careful, your N900 could crash).
There's some documentation in the dbus-scripts config file
There's also a configurator program dbus-scripts-settings written by Matan Ziv-Av. You can use dbus-scripts-settings settings to configure dbus-scripts or just to browse. Thanks to Graham and Matan.
Contents
|
[edit] Configuration
[edit] Basic configuration
Configuration files for dbus-scripts are in /etc/dbus-scripts.d
and you can find some documentation in /etc/dbus-scripts.d/dbus-scripts-example
:
# Example of dbus-scripts control file # # Each line of these files represents a filter to call a script. # Tokens are separated by white space. # First token on line is the script to execute. # Subsequent tokens are filters to match arguments from dbus message. # Filters are matched like shell wildcards (using fnmatch(3)). # If a filter is specified (even if it is *), the corresponding argument # must be present in order to get a match. # # First argument is sender # Second argument is destination # For SIGNAL and METHOD_CALL, third argument is interface # For SIGNAL and METHOD_CALL, fourth argument is member # Other arguments depend on the message # # Example: to act on WLAN interface state changes use: #/some/script * * com.nokia.icd status_changed * WLAN_INFRA # In /some/script, $5 will be the interface name and $7 the new state (IDLE, CONNECTED, etc.)
- this rule will match all wlan network events
/some/script * * com.nokia.icd status_changed * WLAN_INFRA
- this one will match all events for wlan network id of 91f493fb-7c89-4fc6-ac2c-b822923dde45, it's mine
You can find the wlan id with the command
gconftool-2 -R /system/osso/connectivity/IAP
/some/script * * com.nokia.icd status_changed 91f493fb-7c89-4fc6-ac2c-b822923dde45 WLAN_INFRA
- this one will match only the CONNECTED (wifi works) dbus event for my wlan network :
/some/script * * com.nokia.icd status_changed 91f493fb-7c89-4fc6-ac2c-b822923dde45 WLAN_INFRA CONNECTED
[edit] dbus-scripts-settings
There's also a configurator program [1] written by Matan Ziv-Av. You can use dbus-scripts-settings settings to configure dbus-scripts or just to browse.
- As of 2010-07-16, dbus-scripts-settings is missing a dependency on package 'gnome-python-dev'. If the program crashes and at command line you get the error
Traceback (most recent call last): File "/usr/bin/dbus-scripts-settings", line 5, in <module> from gnome import gconf ImportError: No module named gnome
- then the fix is to install gnome-python-dev. magick777 14:09, 16 July 2010 (UTC)
[edit] dbus-scripts daemon options
[edit] Running dbus-scripts on the session bus
By default, the dbus-scripts daemon listens on the system message bus, which reports system events but not those from the user session. If you want to access events such as an application being launched, you'll need to configure the daemon to listen to the session bus as well. You'll probably want to run a second instance of dbus-scripts without breaking the one on the system bus.
Ignore /etc/init.d/dbus-scripts
and /etc/default/dbus-scripts
as these would seem to have been superseded by upstart. Make a copy of /etc/event.d/dbus-scripts
as /etc/event.d/dbus-scripts-session
and edit it so your /etc/event.d/dbus-scripts-session
will look like this:
Nokia-N900:~# cat /etc/event.d/dbus-scripts description "dbus-scripts-session" author "Graham Cobb <g+770@cobb.uk.net>" start on started hildon-desktop #stop on stopping hildon-desktop stop on starting shutdown console none pre-start script /usr/bin/run-standalone.sh /usr/sbin/waitdbus session end script exec run-standalone.sh /usr/sbin/dbus-scripts --session respawn
and you can then configure dbus-scripts with statements such as
#:Browser launched /usr/bin/onbrowser * * com.nokia.HildonDesktop.AppMgr LaunchApplication browser
to react to, in this example, the browser being started.
[edit] The incredibly useful --debug flag
The --debug flag is essential for looking at what's going on if you plan to write your own scripts. Run "dbus-scripts --debug" in a terminal and then perform whatever actions on the phone, to see all the events you can process using dbus-scripts. To see events on the session bus, make that "dbus-scripts --debug --session" (but you will need to run it through run-standalone.sh
). For example:
sudo dbus-scripts --debug sudo /usr/bin/run-standalone.sh dbus-scripts --debug --session
[edit] Usage, tips and tricks
dbus-scripts does not reread edited configuration automatically. After editing (or creating/removing) a configuration file, you need to restart it.
On N900 it can easily be done with
killall dbus-scripts
since it is then restarted by upstart. On 770/N800/N810, use
/etc/init.d/dbus-scripts restart
The package dbus-scripts-settings contains a script logit.sh
which simply logs its parameters to the file /media/mmc1/logit.log
. If you want to find out if some event generates D-Bus events, and what configuration line to use for it, create a file in /etc/dbus-scripts.d/
containing:
/usr/bin/logit.sh *
And generate the event. Reading the log file will show you all the D-Bus activity.
[edit] Limitations
For the moment dbus-scripts will only pass simple D-Bus type arguments like string, boolean and int32 et uint32 to your scripts. You will miss variant, array and all other complex type arguments. So if you want to play, for example, with text of received SMS, you can't yet use dbus-scripts as the text is an array of bytes.
[edit] Some scripts
Here are some things you can do, if you have others, add them. Those examples aren't rocket science and I've stolen some ideas from here and there on web.
[edit] when connect on my wifi home network register only SIP and turn on noisy ring
When I come back home I want my SIP call redirected to my mobile phone and the phone ring turn on and when I leave I want my ring turn off so I wait for D-Bus event on my home wifi network and register/unregister SIP and turn on/off the ring.
I use this dbus-scripts config file :
/etc/dbus-scripts.d/athome
/home/user/bin/athome * * com.nokia.icd status_changed * WLAN_INFRA
and this script to register/unregister SIP and turn on/off the ring.
/home/user/bin/athome
#!/usr/bin/python import dbus import sys import re import telepathy wifi = '91f493fb-7c89-4fc6-ac2c-b822923dde45' # /home/user/bin/w32g * * com.nokia.icd status_changed * WLAN_INFRA * wifi_id,state = sys.argv[5],sys.argv[7] # if we are not on the good wifi network we do nothing and exit if wifi and not re.search(wifi_id, wifi, re.IGNORECASE): sys.exit(0) bus = dbus.SessionBus() account = bus.get_object('org.freedesktop.Telepathy.AccountManager', '/org/freedesktop/Telepathy/Account/sofiasip/sip/_309517129090') profile = bus.get_object('com.nokia.profiled', '/com/nokia/profiled') def change_state(prof, presence_const, presence_text): profile.set_profile(prof, dbus_interface='com.nokia.profiled') account.Set('org.freedesktop.Telepathy.Account', 'RequestedPresence', \ dbus.Struct(( dbus.UInt32( presence_const) , presence_text, ""), signature='uss'), dbus_interface='org.freedesktop.DBus.Properties') if state == "CONNECTED": print "available" change_state('general', telepathy.constants.CONNECTION_PRESENCE_TYPE_AVAILABLE, 'available') elif state == "IDLE": print "offline" change_state('silent', telepathy.constants.CONNECTION_PRESENCE_TYPE_OFFLINE, 'offline') else: sys.exit("Usage: %s (start|stop)" % sys.argv[0])
[edit] turn to 2g when connected to wifi network
There's already an alternative method with fcron. Here is a D-Bus method :
/etc/dbus-scripts.d/w32g
/home/user/bin/w32g.py * * com.nokia.icd status_changed * WLAN_INFRA
Installation documentation is at the beginning of the script and configuration documentation is at the beginning of the configuration file:
/home/user/bin/w32g.py
#!/usr/bin/python import sys import os import ConfigParser import re # You need to install dbus-scripts (BE CAREFUL, it's a devel package) # # as root you need to create a file /etc/sudoers.d/w32g # with this line : # user ALL = NOPASSWD: /bin/ping # and run the command update-sudoers because only root can use ping # # always as root and you need to create another file # /etc/dbus-scripts/w32g with the following line : # # /home/user/bin/w32g * * com.nokia.icd status_changed * WLAN_INFRA * # # and copy this script in /home/user/bin/w32g conf_file = '/home/user/.w32g.conf' config = { 'message_on_idle': '3G cellular mode set', 'message_on_connected': '2G (GSM) cellular mode set', 'connection_test': 'sudo /bin/ping -c 1 www.google.com', 'change_on_idle': 'true', 'change_to_dual': 'true', 'wifi': '' } config_parser = ConfigParser.SafeConfigParser() try: config_parser.read(conf_file) for item in config_parser.items('w32g'): config[ item[0] ] = item[1] except ConfigParser.NoSectionError: pass def to_bool(string): if re.search('true', string, re.IGNORECASE): return True config['change_on_idle'] = to_bool(config['change_on_idle']) config['change_to_dual'] = to_bool(config['change_to_dual']) wifi_id,state = sys.argv[5],sys.argv[7] # if we are not on the good wifi network we do nothing and exit if config['wifi'] and not re.search(wifi_id, config['wifi'], re.IGNORECASE): sys.exit(0) dbus_wifi = "dbus-send --system --type=method_call --print-reply --dest=com.nokia.phone.net /com/nokia/phone/net Phone.Net.set_selected_radio_access_technology"; dbus_notif = "dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.Notifications /org/freedesktop/Notifications org.freedesktop.Notifications.SystemNoteInfoprint"; if state == 'CONNECTED': # we verified that the connection works if config['connection_test']: ret = os.system(config['connection_test']); else: ret = 0 if ret == 0: os.system(dbus_wifi + " byte:1"); os.system(dbus_notif + " string:'" + config['message_on_connected'] + "'") elif state == 'IDLE' and config['change_on_idle']: dual = "2" if config['change_to_dual']: dual = "0" print dual os.system(dbus_wifi + " byte:" + dual); os.system(dbus_notif + " string:'" + config['message_on_idle'] + "'")
/home/user/.w32g.conf
[w32g] ### the comment reflect the default configuration ### true is true (case insensitive), everything else is false ### the command used to test the wifi connection before downgrading to 2g ### if you don't want to do test just delete after = # connection_test = sudo /bin/ping -c 1 www.google.com ### notification message when going to 3g # message_on_idle = 3G cellular mode set ### notification message when going to 2g # message_on_connected = 2G (GSM) cellular mode set ### shall we go back to 3g when wlan disconnect ? #### if you want to have false just delete after = # change_on_idle = true ### shall we go back to dual or 3g ? # change_to_dual = true ### if you want go to 2g only when connected to some wlan you can add their wlan id here ### you can find the wlan id using the command ### gconftool-2 -R /system/osso/connectivity/IAP ### For example : ### wifi = 91f493fb-7c89-4fc6-ac2c-b822923dde45 9ee5dd55-9a32-4ee9-9131-c464ad31d907 # wifi =
[edit] Enable tethering when connecting to Bluetooth PAN
Tethering is a bit of a hassle in Maemo. Most people refer to JoikuSpot, which is definitely the most comfortable solution, but it's not free. So, why not use builtin (or easily installable) functionality to use the N900 as a modem?
For this script to work, you need to have at least bluetooth-dun and iptables installed. Possibly other packages too, please test and correct.
/etc/dbus-scripts.d/tethering
/usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceAdded /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceRemoved
/usr/bin/tethering.sh
#!/bin/sh # # /usr/bin/tethering.sh # # Enable tethering on USB network and Bluetooth PAN. # # Note that the INETDEV must be up and running when this script is # started, or forwarding will not be enabled. # Having tethering on multiple devices concurrently is possible, # but forwarding will be disabled once the first device disconnetcs. # A better check is necessary. # # Put this into /etc/dbus-scripts/tethering : # /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceAdded # /usr/bin/tethering.sh * * org.freedesktop.Hal.Manager DeviceRemoved # # dbus-scripts doesn't give us the event path, so we have to use a more # convoluted method of getting the network interface. # EVENT="$4" UDI="$5" HALDEV="$(echo $UDI | sed 's#.*/\([0-9a-zA-Z_]*\)#\1#')" LOG="/tmp/tethering.log" INETDEV="gprs0" RUNFILE="/var/run/tethering.$HALDEV.pid" IFACEFILE="/var/run/tethering.$HALDEV.iface" if [ "$EVENT" = "DeviceAdded" ]; then IFACE=$(hal-get-property --udi $UDI --key net.interface) case $IFACE in bnep0 ) IF_ADDRESS=192.168.254.254 IF_RANGE=192.168.254.1,192.168.254.254 ;; usb0 ) IF_ADDRESS=192.168.253.254 IF_RANGE=192.168.253.1,192.168.253.254 ;; * ) exit 0 ;; esac echo "$(date) Enabling tethering on interface $IFACE" >> $LOG echo "$IFACE" > $IFACEFILE ifconfig "$IFACE" "$IF_ADDRESS" /sbin/modprobe ipt_MASQUERADE /usr/sbin/iptables -t nat -A POSTROUTING -o $INETDEV -j MASQUERADE start-stop-daemon -S -p "$RUNFILE" -m -b -x /usr/sbin/dnsmasq -- -k -I lo -i "$IFACE" -a $IF_ADDRESS -z -F $IF_RANGE,3600 echo 1 > /proc/sys/net/ipv4/conf/$IFACE/forwarding echo 1 > /proc/sys/net/ipv4/conf/$INETDEV/forwarding fi if [ "$EVENT" = "DeviceRemoved" ]; then if [ ! -f "$IFACEFILE" ]; then exit 0 fi IFACE="$(cat $IFACEFILE)" rm "$IFACEFILE" echo "$(date) Disabling tethering on device $IFACE" >> $LOG echo 0 > /proc/sys/net/ipv4/conf/$INETDEV/forwarding start-stop-daemon -K -p "$RUNFILE" -x /usr/sbin/dnsmasq /usr/sbin/iptables -t nat -D POSTROUTING -o $INETDEV -j MASQUERADE fi exit 0
[edit] Set MSN (pecan) to stay offline when disconnected
Since MSN allows only one client connection at a time, signing into MSN from the N900 disconnects it on my desktop PC, and vice versa.
Pidgin on my desktop PC handles this gracefully by disabling the account until I manually connect it. msn-pecan, on the other hand, helpfully auto-reconnects on its own authority and fails to respect my disabling auto-connect via mc-tool; if the requested status remains as "available", it reconnects, and that disconnects Pidgin on my desktop again. Grr.
You need an instance of dbus-scripts on the session bus for this one (see above), then you can do the following:
[edit] /etc/dbus-scripts.d/dbus-scripts-settings
#:MSN (pecan) disconnected (session bus) /usr/bin/offmsn * * org.freedesktop.DBus ReleaseName org.freedesktop.Telepathy.Connection.pecan.msn_pecan.*
[edit] /usr/bin/offmsn
#!/bin/sh # If we got disconnected from MSN (eg by signing in via Pidgin on desktop PC) then set it offline until further notice mc-tool list | grep "pecan/msn" | awk {'print "mc-tool request "$1" offline"'} | sh
[edit] Adjust Google Voice settings based on network location
I use this script to enable and disable various phones so that my office phone doesn't auto-answer when I'm not there, and my home phone doesn't bother my family when I'm at the office.
To run this script, you will need to install pygooglevoice. Depending on the version of pygooglevoice you have, you may also have to change LOGIN in /usr/local/lib/python2.5/site-packages/googlevoice/settings.py to read:
LOGIN = 'https://accounts.google.com/ServiceLogin?service=grandcentral&passive=1209600&continue=https://www.google.com/voice&followup=https://www.google.com/voice<mpl=open'
as per this bug report.
Then stick this script in /home/user/bin/gvlocation and make it executable:
#!/usr/bin/python import ConfigParser import dbus from googlevoice import Voice from googlevoice.util import LoginError import os import re import signal import sys import time import traceback from urllib2 import URLError # You need to install dbus-scripts (BE CAREFUL, it's a devel package) # # Update Google Voice settings based on location changes. # conf_files = ('/home/user/.gvlocation.conf',) # Default configuration config = \ { 'auth': { 'google_id': None, 'google_pw': None, }, 'configuration': { # Turn on to log activity 'debug': True, # Where to log activity 'log_file': '/home/user/MyDocs/gvl.log', # Time limit for giving up on login attempts 'login_time_limit': 60, # Time limit for giving up on individual operations 'operation_time_limit': 30, # Maximum number of login attempts 'login_limit': 5, # Sleep time between login attempts 'login_sleep': 10, # Whether to use multi-line (persistent) messages for notifications 'persistent_notify': True, # Sections that describe phone numbers (space-separated) 'phones': '', # Sections that describe networks (space-separated) 'networks': '', }, } class Alarm(object): """ Support alarms. For simplicity, only one alarm is allowed at a time. """ def __init__(self, timeout, purpose): """ Create an alarm with a given (floating-point) timeout. For convenience, the timeout can be given as a string. The purpose is a string used in messages if the alarm expires. """ Alarm.__purpose = purpose signal.signal(signal.SIGALRM, Alarm.alarm_handler) # Python 2 doesn't have setitimer #signal.setitimer(signal.ITIMER_REAL, Alarm.__timeout) #Alarm.__timeout = float(timeout) Alarm.__timeout = int(timeout) signal.alarm(Alarm.__timeout) class AlarmError(Exception): def __init__(self, msg): super(Alarm.AlarmError, self).__init__(msg) @staticmethod def alarm_handler(signum, frame): log('%d-second alarm received: %s\n' % (Alarm.__timeout, Alarm.__purpose)) raise Alarm.AlarmError(Alarm.__purpose) def cancel(self): """Cancel an alarm.""" # Python 2 doesn't have setitimer #signal.setitimer(signal.ITIMER_REAL, 0) signal.alarm(0) def main(): log('%s\n%s\n' % (time.ctime(), sys.argv)) parse_config_file() # # Pick up which network we're in. We care only about the ID and state. # network_id, network_type, state = sys.argv[5:8] # # We only take action when we get connected to a network. # if state != 'CONNECTED': sys.exit(0) # # Log in to the Google Voice account. # voice = login_to_voice() if voice is None: sys.exit(1) # # Handle connection to the network. # handle_connection(network_id, voice) def parse_config_file(): """ Parse the configuration file(s) and convert them to a global dictionary. """ global config parser = ConfigParser.SafeConfigParser() parser.read(conf_files) for section in parser.sections(): if section not in config: config[section] = {} for (name, value) in parser.items(section): config[section][name] = value # # Ensure that some basic stuff is given # for var in ('google_id', 'google_pw'): if config['auth'][var] is None or config['auth'][var] == '': log('Missing required authorization variable %s\n' % var, True, True) # # Convert the lists of phones and networks. For phones, we # insist on commma as a separator because people often use # blanks as a readability character. # config['configuration']['phones'] = \ [p.strip() for p in config['configuration']['phones'].split(',') \ if p.strip() != ''] config['configuration']['networks'] = \ [n for n in re.split('[, ]', config['configuration']['networks']) \ if n != ''] # # Make sure the phone and network sections have a full set of # keys; this simplifies the rest of the code. # for phone in [config[p] for p in config['configuration']['phones']]: for phone_key in ('number', 'enable_networks', 'disable_networks', 'enable_message', 'disable_message'): if phone_key not in phone: phone[phone_key] = '' if phone['enable_message'] == '': phone['enable_message'] = phone['number'] + ' enabled.' if phone['disable_message'] == '': phone['disable_message'] = phone['number'] + ' disabled.' for network in config['configuration']['networks']: for network_key in ('network_id', 'enable_phones', 'disable_phones', 'active_message'): if network_key not in config[network]: config[network][network_key] = '' if config[network]['active_message'] == '': config[network]['active_message'] = network + ' activated.' def login_to_voice(): """ Login to Google Voice, returning a Voice object or None if failure. """ voice = Voice() # # GPRS connections sometimes get reported before they're # actually ready. So we'll try more than once. But we won't # go forever, because we don't want to totally screw thing up # in flaky-connectivity cases. # logged_in = False for trial in range(int(config['configuration']['login_limit'])): try: alarm = Alarm(config['configuration']['login_time_limit'], 'logging in') voice.login(config['auth']['google_id'], config['auth']['google_pw']) alarm.cancel() log('Logged in on trial #%d\n' % (trial + 1)) logged_in = True break except LoginError, msg: alarm.cancel() log("Voice login failure due to LoginError, retrying: %s\n" % msg) # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" except Alarm.AlarmError, msg: log("Voice login failure due to timeout, retrying: %s\n" % msg) # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" except URLError, msg: alarm.cancel() log("Voice login failure due to URLError, retrying: %s\n" % msg) # Voice gets an AttributeError when an re search fails; this # happens if the HTTP fetch returns incorrect or partial data. # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" except AttributeError, msg: alarm.cancel() log("Voice login failure due to AttributeError, retrying: %s\n" % msg) # Note: Python 2.5 requires this syntax; 2.6+ needs "Exception as msg" except Exception, msg: info = sys.exc_info() alarm.cancel() log("Surprise exception\n") log(''.join(traceback.format_exception(*info))) except: info = sys.exc_info() alarm.cancel() log("Random exception\n") log(''.join(traceback.format_exception(*info))) time.sleep(int(config['configuration']['login_sleep'])) if not logged_in: log("login failed\n") show_notification_dialog("Couldn't log in to Google Voice") return None return voice log_file = None def log(string, flush = True, copy_to_stderr = False): """ Write a string to the configured log file, opening it if necessary. Errors on open are silently suppressed. If 'flush' is true, flush the log information immediately. """ global log_file if copy_to_stderr or sys.stderr.isatty(): sys.stderr.write(string) if not to_boolean(config['configuration']['debug']): return if log_file is None: # # Don't die on open failures # try: log_file = open(config['configuration']['log_file'], 'a') if not sys.stderr.isatty(): sys.stderr.close() sys.stderr = log_file except: log_file = None config['configuration']['debug'] = False log_file.write(string) if flush: log_file.flush() def to_boolean(value): if isinstance(value, (int, bool)): return value value = value.lower() if value in ('1', 'yes', 'true', 'on'): return True elif value in ('0', 'no', 'false', 'off'): return False else: raise ValueError def handle_connection(network, voice): """ Deal with becoming connected to 'network' by manipulating the Google Voice account named by 'voice'. The exact actions to take are given by the phone and network configuration parameters. """ (enable_phone_numbers, disable_phone_numbers, phone_message) = \ handle_phones(network, voice) (enable_network_numbers, disable_network_numbers, network_message) = \ handle_networks(network, voice) enable_numbers = set(enable_phone_numbers + enable_network_numbers) disable_numbers = set(disable_phone_numbers + disable_network_numbers) enable_disable_numbers(network, voice, enable_numbers, disable_numbers) show_notification_dialog(add_message(phone_message, network_message)) def enable_disable_numbers(network, voice, enable_numbers, disable_numbers): """ Enable and disable Google Voice numbers given in the respective lists. """ try: alarm = Alarm(config['configuration']['operation_time_limit'], 'getting phone list failed') phones = voice.phones alarm.cancel() except Alarm.AlarmError, msg: log("failed to get Google phone list\n") show_notification_dialog("Couldn't get Google phone list") sys.exit(1) enable_phones = [x for x in phones for y in enable_numbers \ if y in x.phoneNumber] disable_phones = [x for x in phones for y in disable_numbers \ if y in x.phoneNumber] log('en: %s\ndis: %s\n' % (enable_phones, disable_phones)) for phone in enable_phones: log('enabling %s\n' % phone.phoneNumber) try: alarm = Alarm(config['configuration']['operation_time_limit'], 'enabling phones') phone.enable() alarm.cancel() except Alarm.AlarmError, msg: log("failed to enable %s\n" % phone.phoneNumber) for phone in disable_phones: log('disabling %s\n' % phone.phoneNumber) try: alarm = Alarm(config['configuration']['operation_time_limit'], 'disabling phones') phone.disable() alarm.cancel() except Alarm.AlarmError, msg: log("failed to disable %s: %s\n" % (phone.phoneNumber, msg)) def add_message(original, append): if original == '': return append elif append == '': return original if to_boolean(config['configuration']['persistent_notify']): # # Multiline message. # return original + '\n' + append else: # # Single-line message. # return original + ' ' + append def handle_phones(network, voice): """ Walk through the phone list, finding entries that ask for action when 'network' is connected. Return a triple: a list of numbers to enable, a list to disable, and a message to display. """ enable = [] disable = [] message = '' for phone in [config[p] for p in config['configuration']['phones']]: action = None if network in phone['enable_networks'] \ or phone['enable_networks'] == 'all': action = 'enable' elif network in phone['disable_networks'] \ or phone['disable_networks'] == 'all': action = 'disable' elif 'other' in phone['enable_networks']: action = 'enable' elif 'other' in phone['disable_networks']: action = 'disable' if action == 'enable': enable.append(clean_phone(phone['number'])) message = add_message(message, phone['enable_message']) elif action == 'disable': disable.append(clean_phone(phone['number'])) message = add_message(message, phone['disable_message']) return (enable, disable, message) def handle_networks(network, voice): """ Walk through the network list, finding entries that ask for action when 'network' is connected. Return a triple: a list of numbers to enable, a list to disable, and a message to display. """ action = None enable = [] disable = [] message = '' found_net = False for net in [config[n] for n in config['configuration']['networks']]: if network not in net['network_id'] \ and net['network_id'] != 'all': continue found_net = True message = add_message(message, net['active_message']) phones = [p.strip() for p in net['enable_phones'].split(',')] while '' in phones: phones.remove('') for phone in phones: if phone in config: for num in \ [p.strip() for p in config[phone]['number'].split(',')]: enable.append(clean_phone(num)) else: enable.append(clean_phone(phone)) phones = [p.strip() for p in net['disable_phones'].split(',')] while '' in phones: phones.remove('') for phone in phones: if phone in config: for num in \ [p.strip() for p in config[phone]['number'].split(',')]: disable.append(clean_phone(num)) else: disable.append(clean_phone(phone)) if not found_net: for net in [config[n] for n in config['configuration']['networks']]: if 'other' not in net['network_id']: continue message = add_message(message, net['active_message']) phones = [p.strip() for p in net['enable_phones'].split(',')] while '' in phones: phones.remove('') for phone in phones: if phone in config: for num in \ [p.strip() for p in config[phone]['number'].split(',')]: enable.append(clean_phone(num)) else: enable.append(clean_phone(phone)) phones = [p.strip() for p in net['disable_phones'].split(',')] while '' in phones: phones.remove('') for phone in phones: if phone in config: for num in \ [p.strip() for p in config[phone]['number'].split(',')]: disable.append(clean_phone(num)) else: disable.append(clean_phone(phone)) return (enable, disable, message) def clean_phone(number): """ Clean a phone number by removing readability characters. """ return re.sub(r'[-\(\) \.]', '', number) def show_notification_dialog(message): bus = dbus.SystemBus() iface = dbus.Interface(bus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications'), 'org.freedesktop.Notifications') if to_boolean(config['configuration']['persistent_notify']): iface.SystemNoteDialog(str(message), dbus.UInt32(0), 'Ok') else: iface.SystemNoteInfoprint(message) if __name__ == "__main__": main()
After installing the script, create /etc/dbus-scripts.d/gvlocation:
/home/user/bin/gvlocation * * com.nokia.icd status_changed * * CONNECTED
Finally, install the following configuration file in /home/user/.gvlocation.conf and edit it to add networks and phone numbers as necessary. The simplest change is just to add your office, home, and cellphone numbers, and to add your office and home WiFi network IDs. Much more complex configurations are possible if you need them.
###################################################################### ## CONFIGURATION FILE FOR GVLOCATION ## ## General layout: Sections are introduced by [name]. Within a section, ## the variable = value notation is used. ## ## In all cases, the default value of a variable is given as a ## commented-out line. ## ## There are two required sections: auth and configuration. All other ## sections are user-defined; see the "phones" and "networks" variables ## in the configuration section. ## ###################################################################### ###################################################################### ## AUTHORIZATION PARAMETERS ###################################################################### [auth] ## Login parameters for Google Voice. These are required, and there is ## no default. ## ## Examples: ## google_id = user@example.com ## google_pw = password google_id = user@example.com google_pw = supersecret ###################################################################### ## CONFIGURATION PARAMETERS ###################################################################### [configuration] ## Debug control. Turn on debugging to log progress messages to a log ## file. This can help if you are having trouble getting the script ## to work. # debug = False ## Log file. Debugging messges go here. This file grows (slowly) ## without limit, so you should occasionally remove or truncate it if ## you leave debugging on. #log_file = /home/user/MyDocs/gvl.log ## Time limit for giving up on login attempts. Gvlocation tries several ## times to log in to Google Voice. Each attempt is aborted if it ## hasn't worked after this number of seconds. #login_time_limit = 60 ## Maximum number of login attempts. After this many tries, gvlocation ## just gives up and won't change your Voice settings. #login_limit = 5 ## Sleep time between login attempts. If a login fails, gvlocation will ## wait this long before trying again. That gives the phone a chance ## to recover the network connection. #login_sleep = 10 ## Time limit for giving up on individual operations. This prevents ## gvlocation from getting hung up after it has logged in, for example ## if your phone goes out of range. #operation_time_limit = 30 ## Whether to use multi-line (persistent) messages for notifications. If ## true, notification messages will stay until you acknowledge them. If ## false, they will be temporary. #persistent_notify = True ######################################## ## The two parameters below are the heart of your configuration. ## There are two ways to organize your setup: by phone, or by ## network. You can even intermix them, though I don't recommend it. ## I prefer by-network organization. ## ## In a by-network setup, you basically say "when I'm on network X, ## enable or disable the following phones." So when you're on your ## home network, you'd enable your home phone and disable the office ## phone; at the office it would be vice versa. ## ## In a by-phone setup, you organize things by phones. Your office ## phone might say "enable me when I'm network A, disable me on B." ## It's really just a different way of laying things out. ## ## List of phones. The names give are user-chosen mnemonics that ## match sections below. The names "auth" and "configuration" are ## prohibited. Separate by blanks or commas. ## ## Example: ## phones = samplephone, homephone, officephone ## #phones= ## List of networks. Again, the names are user-chosen separated by ## blanks or commas. ## ## Example: ## networks = homenet, officenet, othernet ## #networks= networks = homenet, officenet, othernet ###################################################################### ## SAMPLE PHONE DEFINITION ###################################################################### ## ## If the phone is in the "phones" list, then every time the ## network changes, gvlocation will consider enabling or disabling it. ## [samplephone] ## Phone number(s) associated with this phone. Readability characters ## (parentheses, blanks, hypens, and periods) are ignored. ## #number= number = 818-555-1212, (213) 555-1212 ## Network IDs on which the phone(s) should be enabled. Separate multiple ## networks by blanks. "all" matches all networks and must appear ## alone. "other" matches all networks that aren't matched by another ## pattern. ## ## You can find the network id using the command ## gconftool-2 -R /system/osso/connectivity/IAP ## or, more simply, by just turning on debugging and examining the log file. ## #enable_networks = enable_networks = 91f493fb-7c89-4fc6-ac2c-b822923dde45 9ee5dd55-9a32-4ee9-9131-c464ad31d907 ## Network IDs on which the phone(s) should be disabled. Separate multiple ## networks by blanks. "All" matches all networks. "Other" matches all ## networks that aren't matched by another pattern. ## #disable_networks= disable_networks = other ## Text of message to display when this phone is enabled. (gvlocation may ## chose to combine this with other messages.) #enable_message = 818-555-1212, (213) 555-1212 enabled. ## Text of message to display when this phone is disabled. (gvlocation may ## chose to combine this with other messages.) #disable_message = 818-555-1212, (213) 555-1212 disabled. ###################################################################### ## SAMPLE PHONE-NUMBER DEFINITION. ###################################################################### ## ## A phone can be defined even if it's not in the "phones" list; this ## lets you use a symbolic name instead of a phone number. ## [homephone] ## Phone number(s) associated with this "phone". See above. #number = number = 213-555-1212 ### Office phone [officephone] number = 310-555-1212 ### Cellphone [cellphone] number = 212-555-1212 ###################################################################### ## HOME NETWORK DEFINITION ###################################################################### [homenet] ## Network ID. You can find the network id using the command ## gconftool-2 -R /system/osso/connectivity/IAP ## or by turning on debugging and examining the log file. ## ## Multiple networks can be specified by separating the IDs with blanks. ## "all" applies to all networks; "other" applies to any network not matched ## by another network section. network_id = 740ca5b8-8c0f-496e-b8cc-a458680a30a4 ## Phone(s) to enable when on this network. The numbers can be ## digit strings (as above) or references to configuration sections; in ## the latter case the "number" option from that section is used. ## separate numbers by blanks. ## ## Example: ## ## enable_phones = 800-555-1212, homephone ## #enable_phones = enable_phones = homephone ## Phone(s) to disable when on this network. See enable_phones. #disable_phones = disable_phones = officephone ## Message to display when this network activates. #active_message = 740ca5b8-8c0f-496e-b8cc-a458680a30a4 activated. active_message = Home phone enabled, office phone disabled. ### Office network [officenet] ## Network ID. network_id = b501d585-ded3-4c7e-88db-153879e1cea0 enable_phones = officephone disable_phones = homephone #active_message = b501d585-ded3-4c7e-88db-153879e1cea0 activated. active_message = Office phone enabled, home phone disabled. ### Other networks [othernet] ## Network ID. This will match any network not matched by another section ## listed in "networks". network_id = other ## Phone number(s) to enable when on this network. #enable_phones = ## Phone number(s) to disable when on this network. #disable_phones = disable_phones = homephone, officephone ## Message to display when this network activates. #active_message = other activated. active_message = Home and office phones disabled.
[edit] Links to other scripts
[edit] automatically register on public wifi network when connected
You can find a script in this post on tmo
[edit] Thanks
Thanks to Graham Cobb for dbus-scripts and Matan for dbus-scripts-settings
- This page was last modified on 31 October 2012, at 08:02.
- This page has been accessed 112,002 times.