1 from Plugins.Plugin import PluginDescriptor
3 from Tools import Notifications
4 from netgrowl import GrowlRegistrationPacket, GrowlNotificationPacket, \
5 GROWL_UDP_PORT, md5_constructor
6 from twisted.internet.protocol import DatagramProtocol
7 from twisted.internet import reactor
8 from struct import unpack
9 from socket import gaierror
11 from Screens.Setup import SetupSummary
12 from Screens.Screen import Screen
13 from Screens.MessageBox import MessageBox
14 from Components.ActionMap import ActionMap
15 from Components.config import config, getConfigListEntry, ConfigSubsection, \
16 ConfigText, ConfigPassword, ConfigYesNo
17 from Components.ConfigList import ConfigListScreen
18 from Components.Sources.StaticText import StaticText
20 config.plugins.growlee = ConfigSubsection()
21 config.plugins.growlee.enable_incoming = ConfigYesNo(default=False)
22 config.plugins.growlee.enable_outgoing = ConfigYesNo(default=False)
23 config.plugins.growlee.address = ConfigText(fixed_size=False)
24 config.plugins.growlee.password = ConfigPassword()
26 class GrowleeConfiguration(Screen, ConfigListScreen):
27 def __init__(self, session):
28 Screen.__init__(self, session)
29 self.skinName = [ "GrowleeConfiguration", "Setup" ]
32 self["key_red"] = StaticText(_("Cancel"))
33 self["key_green"] = StaticText(_("OK"))
36 self.setup_title = "Growlee Configuration"
37 self.onChangedEntry = []
40 self["actions"] = ActionMap(["SetupActions"],
42 "cancel": self.keyCancel,
47 ConfigListScreen.__init__(
50 getConfigListEntry(_("Receive Notifications?"), config.plugins.growlee.enable_incoming),
51 getConfigListEntry(_("Send Notifications?"), config.plugins.growlee.enable_outgoing),
52 getConfigListEntry(_("Address"), config.plugins.growlee.address),
53 getConfigListEntry(_("Password"), config.plugins.growlee.password),
56 on_change=self.changed
63 for x in self.onChangedEntry:
66 def getCurrentEntry(self):
67 return self["config"].getCurrent()[0]
69 def getCurrentValue(self):
70 return str(self["config"].getCurrent()[1].getText())
72 def createSummary(self):
76 if self["config"].isChanged():
79 def maybeConnect(*args, **kwargs):
80 if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
82 port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
84 d = port.stopListening()
86 d.addCallback(maybeConnect).addErrback(emergencyDisable)
89 elif config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
90 port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
95 def configuration(session, **kwargs):
96 session.open(GrowleeConfiguration)
98 def emergencyDisable(*args, **kwargs):
104 if gotNotification in Notifications.notificationAdded:
105 Notifications.notificationAdded.remove(gotNotification)
106 Notifications.AddPopup(
107 _("Network error.\nDisabling Growlee!"),
108 MessageBox.TYPE_ERROR,
112 class GrowlProtocolOneWrapper(DatagramProtocol):
113 def startProtocol(self):
114 if config.plugins.growlee.enable_outgoing.value:
115 addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
116 p = GrowlRegistrationPacket(password=config.plugins.growlee.password.value)
119 self.transport.write(p.payload(), addr)
123 def sendNotification(self, *args, **kwargs):
124 if not self.transport or not config.plugins.growlee.enable_outgoing.value:
127 addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
128 p = GrowlNotificationPacket(*args, **kwargs)
130 self.transport.write(p.payload(), addr)
134 def datagramReceived(self, data, addr):
136 if Len < 16 or not config.plugins.growlee.enable_incoming.value:
140 password = config.plugins.growlee.password.value
141 checksum = md5_constructor()
142 checksum.update(data[:-16])
144 checksum.update(password)
145 if digest != checksum.digest():
149 if data[1] == '\x01':
150 nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
151 notification, title, description = unpack(("%ds%ds%ds") % (nlen, tlen, dlen), data[12:Len-alen-16])
153 # XXX: we should add a proper fix :-)
154 Notifications.notificationAdded.remove(gotNotification)
155 Notifications.AddPopup(
156 title + '\n' + description,
157 MessageBox.TYPE_INFO,
160 Notifications.notificationAdded.insert(0, gotNotification)
162 # TODO: do we want to handle register packets? :-)
164 growlProtocolOneWrapper = GrowlProtocolOneWrapper()
167 def gotNotification():
168 notifications = Notifications.notifications
170 _, screen, args, kwargs, _ = notifications[-1]
171 if screen is MessageBox:
174 description = kwargs["text"]
176 description = args[0]
177 description = description.decode('utf-8')
179 growlProtocolOneWrapper.sendNotification(title="Dreambox", description=description, password=config.plugins.growlee.password.value)
181 def autostart(**kwargs):
182 if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
184 port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
186 # NOTE: we need to be the first one to be notified since other listeners
187 # may remove the notifications from the list for good
188 Notifications.notificationAdded.insert(0, gotNotification)
190 def Plugins(**kwargs):
193 where=PluginDescriptor.WHERE_SESSIONSTART,
198 description=_("Configure Growlee"),
199 where=PluginDescriptor.WHERE_PLUGINMENU,