better way to ignore messages received by remote daemon
[vuplus_dvbapp-plugin] / growlee / src / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2
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
10
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
19
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()
25
26 NOTIFICATIONID = 'GrowleeReceivedNotification'
27
28 class GrowleeConfiguration(Screen, ConfigListScreen):
29         def __init__(self, session):
30                 Screen.__init__(self, session)
31                 self.skinName = [ "GrowleeConfiguration", "Setup" ]
32
33                 # Buttons
34                 self["key_red"] = StaticText(_("Cancel"))
35                 self["key_green"] = StaticText(_("OK"))
36
37                 # Summary
38                 self.setup_title = "Growlee Configuration"
39                 self.onChangedEntry = []
40
41                 # Define Actions
42                 self["actions"] = ActionMap(["SetupActions"],
43                         {
44                                 "cancel": self.keyCancel,
45                                 "save": self.keySave,
46                         }
47                 )
48
49                 ConfigListScreen.__init__(
50                         self,
51                         [
52                                 getConfigListEntry(_("Receive Notifications?"), config.plugins.growlee.enable_incoming),
53                                 getConfigListEntry(_("Send Notifications?"), config.plugins.growlee.enable_outgoing),
54                                 getConfigListEntry(_("Address"), config.plugins.growlee.address),
55                                 getConfigListEntry(_("Password"), config.plugins.growlee.password),
56                         ],
57                         session=session,
58                         on_change=self.changed
59                 )
60
61                 # Trigger change
62                 self.changed()
63
64         def changed(self):
65                 for x in self.onChangedEntry:
66                         x()
67
68         def getCurrentEntry(self):
69                 return self["config"].getCurrent()[0]
70
71         def getCurrentValue(self):
72                 return str(self["config"].getCurrent()[1].getText())
73
74         def createSummary(self):
75                 return SetupSummary
76
77         def keySave(self):
78                 if self["config"].isChanged():
79                         global port
80                         if port:
81                                 def maybeConnect(*args, **kwargs):
82                                         if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
83                                                 global port
84                                                 port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
85
86                                 d = port.stopListening()
87                                 if d is not None:
88                                         d.addCallback(maybeConnect).addErrback(emergencyDisable)
89                                 else:
90                                         maybeConnect()
91                         elif config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
92                                 port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
93
94                 self.saveAll()
95                 self.close()
96
97 def configuration(session, **kwargs):
98         session.open(GrowleeConfiguration)
99
100 def emergencyDisable(*args, **kwargs):
101         global port
102         if port:
103                 port.stopListening()
104                 port = None
105
106         if gotNotification in Notifications.notificationAdded:
107                 Notifications.notificationAdded.remove(gotNotification)
108         Notifications.AddPopup(
109                 _("Network error.\nDisabling Growlee!"),
110                 MessageBox.TYPE_ERROR,
111                 10
112         )
113
114 class GrowlProtocolOneWrapper(DatagramProtocol):
115         def startProtocol(self):
116                 if config.plugins.growlee.enable_outgoing.value:
117                         addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
118                         p = GrowlRegistrationPacket(password=config.plugins.growlee.password.value)
119                         p.addNotification()
120                         try:
121                                 self.transport.write(p.payload(), addr)
122                         except gaierror:
123                                 emergencyDisable()
124
125         def sendNotification(self, *args, **kwargs):
126                 if not self.transport or not config.plugins.growlee.enable_outgoing.value:
127                         return
128
129                 addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
130                 p = GrowlNotificationPacket(*args, **kwargs)
131                 try:
132                         self.transport.write(p.payload(), addr)
133                 except gaierror:
134                         emergencyDisable()
135
136         def datagramReceived(self, data, addr):
137                 Len = len(data)
138                 if Len < 16 or not config.plugins.growlee.enable_incoming.value:
139                         return
140
141                 digest = data[-16:]
142                 password = config.plugins.growlee.password.value
143                 checksum = md5_constructor()
144                 checksum.update(data[:-16])
145                 if password:
146                         checksum.update(password)
147                 if digest != checksum.digest():
148                         return
149
150                 # notify packet
151                 if data[1] == '\x01':
152                         nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
153                         notification, title, description = unpack(("%ds%ds%ds") % (nlen, tlen, dlen), data[12:Len-alen-16])
154
155                         Notifications.AddNotificationWithID(
156                                 NOTIFICATIONID,
157                                 MessageBox,
158                                 text = title + '\n' + description,
159                                 type = MessageBox.TYPE_INFO,
160                                 timeout = 5,
161                                 close_on_any_key = True,
162                         )
163
164                 # TODO: do we want to handle register packets? :-)
165
166 growlProtocolOneWrapper = GrowlProtocolOneWrapper()
167 port = None
168
169 def gotNotification():
170         notifications = Notifications.notifications
171         if notifications:
172                 _, screen, args, kwargs, id = notifications[-1]
173                 if screen is MessageBox and id != NOTIFICATIONID:
174
175                         if "text" in kwargs:
176                                 description = kwargs["text"]
177                         else:
178                                 description = args[0]
179                         description = description.decode('utf-8')
180
181                         growlProtocolOneWrapper.sendNotification(title="Dreambox", description=description, password=config.plugins.growlee.password.value)
182
183 def autostart(**kwargs):
184         if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
185                 global port
186                 port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
187
188         # NOTE: we need to be the first one to be notified since other listeners
189         # may remove the notifications from the list for good
190         Notifications.notificationAdded.insert(0, gotNotification)
191
192 def Plugins(**kwargs):
193         return [
194                 PluginDescriptor(
195                         where=PluginDescriptor.WHERE_SESSIONSTART,
196                         fnc=autostart,
197                 ),
198                 PluginDescriptor(
199                         name="Growlee",
200                         description=_("Configure Growlee"), 
201                         where=PluginDescriptor.WHERE_PLUGINMENU,
202                         fnc=configuration,
203                 ),
204         ]
205