Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 239 lines (214 sloc) 8.08 KB
#!/usr/bin/python
#
# Watch p2000 alarms and notify for new alarms
#
# This program displays p2000 pager messages. p2000 is the pager system for the
# dutch emergency services. Messages are downloaded from p2000-online.net every
# so often (default: 60 seconds). This website also has a delay of about 90
# seconds, making the total delay approximately 2.5 minutes.
#
# Requirements:
# - A linux desktop
# - pynotify and gtk to display notifications
# - beautifulsoup for parsing html
# - libcanberra to play a sound when new alarms arrive (optional)
#
# (c)2011 Dennis Kaarsemaker
# License: gpl 3+
from BeautifulSoup import BeautifulSoup as soup
import ctypes
import os
import glib
import gtk
import pynotify
import sys
import time
import urllib
# These are the region names as used by p2000-online.net
regions = (
"Amsterdam-Amstelland",
"Brabant Noord",
"Brabant ZuidOost",
"Drenthe",
"Flevoland",
"Friesland",
"Gelderland-Midden",
"Gelderland-Zuid",
"Gooi en Vechtstreek",
"Groningen",
"Haaglanden",
"Hollands Midden",
"IJsselland",
"Kennemerland",
"Limburg Noord",
"Limburg Zuid",
"Midden en West Brabant",
"Noord- en Oost Gelderland",
"Noord-Holland Noord",
"Rotterdam Rijnmond",
"Twente",
"Utrecht",
"Zaanstreek-Waterland",
"Zeeland",
"Zuid Holland Zuid",
)
def _escape(data):
return data.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')
class I(dict):
def __getitem__(self, item):
return 'http://alarmeringen.nl/static/images/icon_%s.png' % dict.__getitem__(self, item)
class Alert(object):
"""Representation of a single message"""
icons = I({
'brandweer': 'fire',
'politie': 'police',
'ambulance': 'ambu',
'lifeliner': 'heli',
'knrm': 'boat',
})
def __init__(self, date, time, who, region, text, recipients = None):
self.time = '%s %s' % (date, time)
self.who = who
self.region = region
self.text = text
self.recipients = recipients or []
if self.is_lifeliner():
self.who = 'LifeLiner'
def is_lifeliner(self):
return 'lifeliner' in (''.join(self.recipients)).lower()
def show(self, timeout):
"""Display a notification for this alarm"""
title = "%s %s" % (self.who, self.region)
text = "%s %s" % (self.time, self.text)
mod = self.__module__
if mod == '__main__':
path = sys.argv[0]
else:
path = sys.modules[mod].__file__
path = os.path.abspath(os.path.dirname(path))
imgl = self.icons[self.who.lower()]
imgc = os.path.join(os.path.expanduser('~'), '.cache', os.path.basename(imgl))
if not os.path.exists(imgc):
with open(imgc, 'w') as fd:
req = urllib.urlopen(img)
if req.code == 404:
raise IOError
fd.write(req.read())
notification = pynotify.Notification(title, _escape(text), 'file://' + imgc)
notification.set_urgency(pynotify.URGENCY_CRITICAL)
caps = pynotify.get_server_caps()
if self.recipients and 'actions' in caps:
notification.add_action("details", "Details", self.details)
# If the notification is deleted, the callback will not be called
_alerts.append(notification)
notification.set_timeout(timeout * 1000)
notification.show()
def prnt(self):
"""Output this alarm on stdout"""
print "%s %s %s %s" % (time.ctime(), self.time, self.who, self.region)
print "%s %s %s" % (time.ctime(), ' ' * len(self.time), self.text)
for r in self.recipients:
print "%s %s %s" % (time.ctime(), ' ' * len(self.time), r)
def details(self, notification, action):
message = '<b>%s</b>\n%s\n<span foreground="gray">%s</span>' % (self.text, self.time, '\n'.join(self.recipients))
dialog = gtk.MessageDialog(type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_CLOSE)
dialog.set_markup(message)
dialog.set_title('%s %s' % (self.who, self.region))
dialog.connect("response", lambda *args: dialog.destroy())
dialog.show()
dialog.set_keep_above(True)
_alerts.remove(notification)
_alerts = []
class P2000Interface(object):
def __init__(self, opts):
self.regions = opts.regions
self.verbose = opts.verbose
self.last_alert = None
self.quiet = opts.quiet
if not self.quiet:
try:
self.canberra = ctypes.CDLL('libcanberra.so.0')
self.ctx = ctypes.c_void_p()
self.canberra.ca_context_create(ctypes.byref(self.ctx))
except OSError:
# Can't find libcanberra
self.quiet = True
pynotify.init("p2000 meldingen")
def get_alerts(self):
"""Fetch all alerts for these regions"""
url = 'http://www.p2000-online.net/p2000.php?%s&nofilterform=1'
url = url % '&'.join(['%s=1' % x for x in self.regions])
if self.verbose:
print time.ctime(), url
try:
data = urllib.urlopen(url).read()
except IOError:
if self.verbose:
import traceback
traceback.print_exc()
return []
doc = soup(data)
alerts = []
table = doc.body('table', {'style': 'align:center'})[0]
for tr in table('tr'):
if tr.td.get('class', None) == 'DT':
alerts.append(Alert(*[x.text for x in tr('td')]))
else:
recipient = tr('td')[-1].text
if recipient != '&nbsp;':
alerts[-1].recipients.append(recipient)
return alerts
def alert_iteration(self, first_iteration = False):
global _alerts
_alerts = []
if self.verbose:
print time.ctime(), "Fetching alerts..."
alerts = self.get_alerts()
if self.verbose:
print time.ctime(), "Received %d alerts" % len(alerts)
play_sound = True
alerts = [x for x in alerts[:5] if x.time > self.last_alert]
alerts.reverse()
timeout = min(10,3*len(alerts))
if self.verbose:
print "Timeout: %d" % timeout
for alert in alerts:
if play_sound:
self.canberra.ca_context_play(self.ctx, 1, "event.id", "message-new-instant", None)
play_sound = False
if self.verbose:
alert.prnt()
alert.show(timeout)
self.last_alert = alert.time
return not first_iteration
def main():
import optparse
usage = """%prog [options]
This tool autimatically downloads and displays p2000 alerts for the regions you
specify (Default: Noord-Holland Noord and Flevoland). It can display images and
play sounds too."""
p = optparse.OptionParser(usage = usage)
default_regions = ("Flevoland","Noord-HollandNoord")
p.add_option('-a', '--all', dest='all_regions', action='store_true', default=False,
help="Show alerts for all regions")
for r in regions:
opt_r = '--%s' % r.lower().replace('-','').replace(' ','')
p.add_option(opt_r, dest='regions', action='append_const', const=r.replace(' ',''),
help="Show alerts for region %s" % r)
p.add_option('-d', '--delay', dest='delay', type='int', default=60, metavar='SECS',
help="Delay between updates (default: 60)")
p.add_option('-v', '--verbose', dest='verbose', action='store_true', default=False,
help="Verbose output")
p.add_option('-q', '--quiet', dest='quiet', action='store_true', default=False,
help="Don't play sounds when new alarms arrive")
opts, args = p.parse_args()
if not opts.regions:
opts.regions = default_regions
if opts.all_regions:
opts.regions = [x.replace(' ','') for x in regions]
ui = P2000Interface(opts)
glib.timeout_add_seconds(0, ui.alert_iteration, True)
glib.timeout_add_seconds(opts.delay, ui.alert_iteration)
gtk.main()
if __name__ == '__main__':
main()