implement protocol by ourselves
[vuplus_dvbapp-plugin] / growlee / src / GrowlTalk.py
1 from twisted.internet.protocol import DatagramProtocol
2 from twisted.internet import reactor
3 from struct import pack, unpack
4 from hashlib import md5
5
6 from Screens.MessageBox import MessageBox
7 from Tools import Notifications
8 from Components.config import config
9
10 from GrowleeConnection import emergencyDisable
11 from . import NOTIFICATIONID
12
13 GROWL_UDP_PORT = 9887
14
15 class GrowlTalk(DatagramProtocol):
16         addr = None
17
18         def gotIP(self, ip):
19                 self.addr = (ip, GROWL_UDP_PORT)
20                 if config.plugins.growlee.enable_outgoing.value:
21                         p = pack("!BBHBB",
22                                         1, # version
23                                         0, # registration
24                                         7, # length of application name == len("growlee")
25                                         1, # one notification
26                                         1, # one of them default
27                         )
28                         p += "growlee" # application name
29                         p += pack("!H",
30                                         32, # length of first notification type name
31                         )
32                         p += "Notifications from your Dreambox" # first notification type name
33                         p += "\x00" # index of default notifications
34
35                         password = config.plugins.growlee.password.value
36                         checksum = md5()
37                         checksum.update(p)
38                         if password:
39                                 checksum.update(password)
40                         p += checksum.digest()
41
42                         self.transport.write(p, self.addr)
43
44         def noIP(self, error):
45                 print "--------------------------------", error
46                 emergencyDisable()
47
48         def startProtocol(self):
49                 reactor.resolve(config.plugins.growlee.address.value).addCallback(self.gotIP).addErrback(self.noIP)
50
51         def sendNotification(self, title='No title.', description='No description.', flags=0):
52                 if not self.transport or not self.addr or not config.plugins.growlee.enable_outgoing.value:
53                         return
54
55                 p = pack("!BBHHHHH",
56                                 1, # version
57                                 1, # notification
58                                 flags, # 3-bit signed priority, 1 bit sticky in rightmost nibble
59                                 32, # len("Notifications from your Dreambox")
60                                 len(title),
61                                 len(description),
62                                 7, # len("growlee")
63                 )
64                 p += "Notifications from your Dreambox"
65                 p += title
66                 p += description
67                 p += "growlee"
68
69                 password = config.plugins.growlee.password.value
70                 checksum = md5()
71                 checksum.update(p)
72                 if password:
73                         checksum.update(password)
74                 p += checksum.digest()
75
76                 self.transport.write(p, self.addr)
77
78         def datagramReceived(self, data, addr):
79                 if not config.plugins.growlee.enable_incoming.value:
80                         return
81
82                 Len = len(data)
83                 if Len < 16:
84                         return
85
86                 # ver == GROWL_PROTOCOL_VERSION
87                 if data[0] != '\x01':
88                         return
89
90                 # type == GROWL_TYPE_NOTIFICATION
91                 if data[1] == '\x01':
92                         digest = data[-16:]
93                         password = config.plugins.growlee.password.value
94                         checksum = md5()
95                         checksum.update(data[:-16])
96                         if password:
97                                 checksum.update(password)
98                         if digest != checksum.digest():
99                                 return
100
101                         nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
102                         notification, title, description = unpack("%ds%ds%ds" % (nlen, tlen, dlen), data[12:Len-alen-16])
103                 # type == GROWL_TYPE_NOTIFICATION_NOAUTH
104                 elif data[1] == '\x05':
105                         nlen, tlen, dlen, alen = unpack("!HHHH",str(data[4:12]))
106                         notification, title, description = unpack("%ds%ds%ds" % (nlen, tlen, dlen), data[12:Len-alen])
107                 else:
108                         # don't handle any other packet yet
109                         return
110
111                 Notifications.AddNotificationWithID(
112                         NOTIFICATIONID,
113                         MessageBox,
114                         text = title + '\n' + description,
115                         type = MessageBox.TYPE_INFO,
116                         timeout = 5,
117                         close_on_any_key = True,
118                 )
119
120 class GrowlTalkAbstraction:
121         def __init__(self):
122                 self.growltalk = GrowlTalk()
123                 self.serverPort = reactor.listenUDP(GROWL_UDP_PORT, self.growltalk)
124
125         def sendNotification(self, title='No title.', description='No description.', priority=-1, timeout=-1):
126                 if priority < 0:
127                         flags = 8 + (-priority << 1)
128                 else:
129                         flags = priority << 1
130
131                 # NOTE: sticky didn't work in any of my tests, but let's assume this is my configurations fault
132                 if timeout == -1:
133                         flags |= 1
134
135                 self.growltalk.sendNotification(title=title, description=description, flags=flags)
136
137         def stop(self):
138                 return self.serverPort.stopListening()
139