filebrowser \
fritzcall \
ftpbrowser \
- googlemaps \
genuinedreambox \
+ googlemaps \
+ growlee \
httpproxy \
imdb \
kiddytimer \
ftpbrowser/po/Makefile
ftpbrowser/src/Makefile
ftpbrowser/src/images/Makefile
+
+growlee/Makefile
+growlee/src/Makefile
])
--- /dev/null
+Package: enigma2-plugin-extensions-growlee
+Version: 0.1-20091001-r0
+Description: redirect notifications to growl
+Architecture: mipsel
+Section: extra
+Priority: optional
+Maintainer: Moritz Venn <moritz.venn@freaque.net>
+Homepage: http://www.ritzmo.de
+Depends: enigma2(>=2.6git20090615), twisted
+Source: http://enigma2-plugins.schwerkraft.elitedvb.net/
--- /dev/null
+SUBDIRS = src\r
--- /dev/null
+All Files of this Software are licensed under the Creative Commons
+Attribution-NonCommercial-ShareAlike 3.0 Unported
+License if not stated otherwise in a Files Head. To view a copy of this license, visit
+http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
+Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+
+Alternatively, this plugin may be distributed and executed on hardware which
+is licensed by Dream Multimedia GmbH.
+
+This plugin is NOT free software. It is open source, you are allowed to
+modify it (if you keep the license), but it may not be commercially
+distributed other than under the conditions noted above.
--- /dev/null
+installdir = /usr/lib/enigma2/python/Plugins/Extensions/Growlee\r
+\r
+install_PYTHON = *.py\r
+install_DATA = maintainer.info LICENSE\r
--- /dev/null
+moritz.venn@freaque.net
+Growlee
--- /dev/null
+#!/usr/bin/env python
+
+"""Growl 0.6 Network Protocol Client for Python"""
+__version__ = "0.6.2"
+__author__ = "Rui Carmo (http://the.taoofmac.com)"
+__copyright__ = "(C) 2004 Rui Carmo. Code under BSD License."
+__contributors__ = "Ingmar J Stein (Growl Team), John Morrissey (hashlib patch)"
+
+try:
+ import hashlib
+ md5_constructor = hashlib.md5
+except ImportError:
+ import md5
+ md5_constructor = md5.new
+
+import struct
+from socket import AF_INET, SOCK_DGRAM, socket
+
+GROWL_UDP_PORT=9887
+GROWL_PROTOCOL_VERSION=1
+GROWL_TYPE_REGISTRATION=0
+GROWL_TYPE_NOTIFICATION=1
+
+class GrowlRegistrationPacket:
+ """Builds a Growl Network Registration packet.
+ Defaults to emulating the command-line growlnotify utility."""
+
+ def __init__(self, application="growlnotify", password = None ):
+ self.notifications = []
+ self.defaults = [] # array of indexes into notifications
+ self.application = application.encode("utf-8")
+ self.password = password
+ # end def
+
+
+ def addNotification(self, notification="Command-Line Growl Notification", enabled=True):
+ """Adds a notification type and sets whether it is enabled on the GUI"""
+ self.notifications.append(notification)
+ if enabled:
+ self.defaults.append(len(self.notifications)-1)
+ # end def
+
+
+ def payload(self):
+ """Returns the packet payload."""
+ self.data = struct.pack( "!BBH",
+ GROWL_PROTOCOL_VERSION,
+ GROWL_TYPE_REGISTRATION,
+ len(self.application) )
+ self.data += struct.pack( "BB",
+ len(self.notifications),
+ len(self.defaults) )
+ self.data += self.application
+ for notification in self.notifications:
+ encoded = notification.encode("utf-8")
+ self.data += struct.pack("!H", len(encoded))
+ self.data += encoded
+ for default in self.defaults:
+ self.data += struct.pack("B", default)
+ self.checksum = md5_constructor()
+ self.checksum.update(self.data)
+ if self.password:
+ self.checksum.update(self.password)
+ self.data += self.checksum.digest()
+ return self.data
+ # end def
+# end class
+
+
+class GrowlNotificationPacket:
+ """Builds a Growl Network Notification packet.
+ Defaults to emulating the command-line growlnotify utility."""
+
+ def __init__(self, application="growlnotify",
+ notification="Command-Line Growl Notification", title="Title",
+ description="Description", priority = 0, sticky = False, password = None ):
+ self.application = application.encode("utf-8")
+ self.notification = notification.encode("utf-8")
+ self.title = title.encode("utf-8")
+ self.description = description.encode("utf-8")
+ flags = (priority & 0x07) * 2
+ if priority < 0:
+ flags |= 0x08
+ if sticky:
+ flags = flags | 0x0100
+ self.data = struct.pack( "!BBHHHHH",
+ GROWL_PROTOCOL_VERSION,
+ GROWL_TYPE_NOTIFICATION,
+ flags,
+ len(self.notification),
+ len(self.title),
+ len(self.description),
+ len(self.application) )
+ self.data += self.notification
+ self.data += self.title
+ self.data += self.description
+ self.data += self.application
+ self.checksum = md5_constructor()
+ self.checksum.update(self.data)
+ if password:
+ self.checksum.update(password)
+ self.data += self.checksum.digest()
+ # end def
+
+ def payload(self):
+ """Returns the packet payload."""
+ return self.data
+ # end def
+# end class
+
+
+if __name__ == '__main__':
+ print "Starting Unit Test"
+ print " - please make sure Growl is listening for network notifications"
+ addr = ("localhost", GROWL_UDP_PORT)
+ s = socket(AF_INET,SOCK_DGRAM)
+ print "Assembling registration packet like growlnotify's (no password)"
+ p = GrowlRegistrationPacket()
+ p.addNotification()
+ print "Sending registration packet"
+ s.sendto(p.payload(), addr)
+
+ print "Assembling standard notification packet"
+ p = GrowlNotificationPacket()
+ print "Sending standard notification packet"
+ s.sendto(p.payload(), addr)
+
+ s.close()
+"""
+ print "Assembling priority -2 (Very Low) notification packet"
+ p = GrowlNotificationPacket(priority=-2)
+ print "Sending priority -2 notification packet"
+ s.sendto(p.payload(), addr)
+
+ print "Assembling priority 2 (Very High) sticky notification packet"
+ p = GrowlNotificationPacket(priority=2,sticky=True)
+ print "Sending priority 2 (Very High) sticky notification packet"
+ s.sendto(p.payload(), addr)
+ s.close()
+ print "Done."
+"""
--- /dev/null
+from Plugins.Plugin import PluginDescriptor
+
+from Tools import Notifications
+from netgrowl import GrowlRegistrationPacket, GrowlNotificationPacket, \
+ GROWL_UDP_PORT, md5_constructor
+from twisted.internet.protocol import DatagramProtocol
+from twisted.internet import reactor
+from struct import unpack
+
+from Screens.Setup import SetupSummary
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Components.ActionMap import ActionMap
+from Components.config import config, getConfigListEntry, ConfigSubsection, \
+ ConfigText, ConfigPassword, ConfigYesNo
+from Components.ConfigList import ConfigListScreen
+
+config.plugins.growlee = ConfigSubsection()
+config.plugins.growlee.enable_incoming = ConfigYesNo(default=False)
+config.plugins.growlee.enable_outgoing = ConfigYesNo(default=False)
+config.plugins.growlee.address = ConfigText(fixed_size=False)
+config.plugins.growlee.password = ConfigPassword()
+
+class GrowleeConfiguration(Screen, ConfigListScreen):
+ skin = """<screen title="Growlee Configuration" position="75,155" size="565,280">
+ <widget name="config" position="5,5" size="555,100" scrollbarMode="showOnDemand" />
+ </screen>"""
+
+ def __init__(self, session):
+ Screen.__init__(self, session)
+
+ # Summary
+ self.setup_title = "Growlee Configuration"
+ self.onChangedEntry = []
+
+ # Define Actions
+ self["actions"] = ActionMap(["SetupActions"],
+ {
+ "cancel": self.keySave,
+ }
+ )
+
+ ConfigListScreen.__init__(
+ self,
+ [
+ getConfigListEntry(_("Receive Notifications?"), config.plugins.growlee.enable_incoming),
+ getConfigListEntry(_("Send Notifications?"), config.plugins.growlee.enable_outgoing),
+ getConfigListEntry(_("Address"), config.plugins.growlee.address),
+ getConfigListEntry(_("Password"), config.plugins.growlee.password),
+ ],
+ session=session,
+ on_change=self.changed
+ )
+
+ # Trigger change
+ self.changed()
+
+ def changed(self):
+ for x in self.onChangedEntry:
+ try:
+ x()
+ except:
+ pass
+
+ def getCurrentEntry(self):
+ return self["config"].getCurrent()[0]
+
+ def getCurrentValue(self):
+ return str(self["config"].getCurrent()[1].getText())
+
+ def createSummary(self):
+ return SetupSummary
+
+ def keySave(self):
+ if self["config"].isChanged():
+ global port
+ if port:
+ port.stopListening()
+
+ if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
+ port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
+
+ self.saveAll()
+ self.close()
+
+def configuration(session, **kwargs):
+ session.open(GrowleeConfiguration)
+
+class GrowlProtocolOneWrapper(DatagramProtocol):
+ def startProtocol(self):
+ if config.plugins.growlee.enable_outgoing.value:
+ addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
+ p = GrowlRegistrationPacket(password=config.plugins.growlee.password.value)
+ p.addNotification()
+ self.transport.write(p.payload(), addr)
+
+ def sendNotification(self, *args, **kwargs):
+ if not self.transport or not config.plugins.growlee.enable_outgoing.value:
+ return
+
+ addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
+ p = GrowlNotificationPacket(*args, **kwargs)
+ self.transport.write(p.payload(), addr)
+
+ def datagramReceived(self, data, addr):
+ Len = len(data)
+ if Len < 16 or not config.plugins.growlee.enable_incoming.value:
+ return
+
+ digest = data[-16:]
+ password = config.plugins.growlee.password.value
+ checksum = md5_constructor()
+ checksum.update(data[:-16])
+ if password:
+ checksum.update(password)
+ if digest != checksum.digest():
+ return
+
+ # notify packet
+ if data[1] == '\x01':
+ nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
+ notification, title, description = unpack(("%ds%ds%ds") % (nlen, tlen, dlen), data[12:Len-alen-16])
+
+ # XXX: we should add a proper fix :-)
+ Notifications.notificationAdded.remove(gotNotification)
+ Notifications.AddPopup(
+ title + '\n' + description,
+ MessageBox.TYPE_INFO,
+ 5
+ )
+ Notifications.notificationAdded.insert(0, gotNotification)
+
+ # TODO: do we want to handle register packets? :-)
+
+growlProtocolOneWrapper = GrowlProtocolOneWrapper()
+port = None
+
+def gotNotification():
+ notifications = Notifications.notifications
+ if notifications:
+ _, screen, args, kwargs, _ = notifications[-1]
+ if screen is MessageBox:
+
+ if "text" in kwargs:
+ description = kwargs["text"]
+ else:
+ description = args[0]
+
+ growlProtocolOneWrapper.sendNotification(title="Dreambox", description=description, password=config.plugins.growlee.password.value)
+
+def autostart(**kwargs):
+ if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
+ global port
+ port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
+
+ # NOTE: we need to be the first one to be notified since other listeners
+ # may remove the notifications from the list for good
+ Notifications.notificationAdded.insert(0, gotNotification)
+
+def Plugins(**kwargs):
+ return [
+ PluginDescriptor(
+ where=PluginDescriptor.WHERE_SESSIONSTART,
+ fnc=autostart,
+ ),
+ PluginDescriptor(
+ name="Growlee",
+ description=_("Configure Growlee"),
+ where=PluginDescriptor.WHERE_PLUGINMENU,
+ fnc=configuration,
+ ),
+ ]
+