7d66c65f4f02afc1b7c41b81f16cf11b6329322d
[vuplus_dvbapp-plugin] / growlee / src / SNP.py
1 from twisted.internet.defer import Deferred
2 from twisted.internet.protocol import ClientFactory, ServerFactory
3 from twisted.internet import reactor
4 from twisted.protocols.basic import LineReceiver
5
6 from Tools import Notifications
7 from Components.config import config
8
9 from GrowleeConnection import emergencyDisable
10 from . import NOTIFICATIONID
11
12 SNP_TCP_PORT = 9887
13
14 class SnarlNetworkProtocol(LineReceiver):
15         def __init__(self, client = False):
16                 self.client = client
17
18         def connectionMade(self):
19                 self.factory.addClient(self)
20                 if self.client:
21                         payload = "type=SNP#?version=1.0#?action=register#?app=growlee"
22                         self.sendLine(payload)
23
24                         payload = "type=SNP#?version=1.0#?action=add_class#?app=growlee#?class=growleeClass#?title=Notifications from your Dreambox"
25                         self.sendLine(payload)
26
27         def connectionLost(self, reason):
28                 self.factory.removeClient(self)
29
30         def stop(self):
31                 if self.client:
32                         payload = "type=SNP#?version=1.0#?action=unregister#?app=growlee"
33                         self.sendLine(payload)
34
35                 self.transport.loseConnection()
36
37         def sendNotification(self, title='No title.', description='No message.', timeout=1):
38                 if not self.client or not self.transport:
39                         return
40
41                 payload = "type=SNP#?version=1.0#?action=notification#?app=growlee#?class=growleeClass#?title=%s#?text=%s#?timeout=%d" % (title, description, timeout)
42                 self.sendLine(payload)
43
44         def lineReceived(self, data):
45                 if self.client or not self.transport:
46                         return
47
48                 Len = len(data)
49                 if Len < 23 or not data[:23] == "type=SNP#?version=1.0#?":
50                         return
51
52                 items = data[23:].split('#?')
53
54                 title = ''
55                 description = ''
56                 timeout = 5
57                 for item in items:
58                         key, value = item.split('=')
59                         if key == "action":
60                                 if value == "unregister":
61                                         payload = "SNP/1.0/0/OK"
62                                         self.sendLine(payload)
63                                         self.transport.loseConnection()
64                                         return
65                                 elif value != "notification":
66                                         # NOTE: we pretend to handle&accept pretty much everything one throws at us
67                                         payload = "SNP/1.0/0/OK"
68                                         self.sendLine(payload)
69                                         return
70                         elif key == "title":
71                                 title = value
72                         elif key == "text":
73                                 description = value
74                         elif key == "timeout":
75                                 timeout = int(value)
76
77                 Notifications.AddNotificationWithID(
78                         NOTIFICATIONID,
79                         MessageBox,
80                         text = title + '\n' + description,
81                         type = MessageBox.TYPE_INFO,
82                         timeout = timeout,
83                         close_on_any_key = True,
84                 )
85
86                 # return ok
87                 payload = "SNP/1.0/0/OK"
88                 self.sendLine(payload)
89
90 class SnarlNetworkProtocolClientFactory(ClientFactory):
91         client = None
92
93         def buildProtocol(self, addr):
94                 p = SnarlNetworkProtocol(client = True)
95                 p.factory = self
96                 return p
97
98         def sendNotification(self, title='No title.', description='No message.', priority=0, timeout=-1):
99                 if self.client:
100                         title = title.decode('utf-8', 'ignore').encode('iso8859-15', 'ignore')
101                         description = description.decode('utf-8', 'ignore').encode('iso8859-15', 'ignore')
102
103                         # NOTE: timeout = 0 means sticky, so add one second to map -1 to 0 and make 0 non-sticky
104                         if timeout < 1:
105                                 timeout += 1
106
107                         self.client.sendNotification(title=title, description=description, timeout=timeout)
108
109         def addClient(self, client):
110                 self.client = client
111
112         def removeClient(self, client):
113                 self.client = None
114
115 class SnarlNetworkProtocolServerFactory(ServerFactory):
116         protocol = SnarlNetworkProtocol
117
118         def __init__(self):
119                 self.clients = []
120
121         def addClient(self, client):
122                 self.clients.append(client)
123
124         def removeClient(self, client):
125                 self.clients.remove(client)
126
127         def sendNotification(self, *args, **kwargs):
128                 pass
129
130         def stopFactory(self):
131                 for client in self.clients:
132                         client.stop()
133
134 class SnarlNetworkProtocolAbstraction:
135         clientPort = None
136         serverPort = None
137         pending = 0
138
139         def __init__(self):
140                 self.clientFactory = SnarlNetworkProtocolClientFactory()
141                 self.serverFactory = SnarlNetworkProtocolServerFactory()
142
143                 if config.plugins.growlee.enable_outgoing.value:
144                         reactor.resolve(config.plugins.growlee.address.value).addCallback(self.gotIP).addErrback(self.noIP)
145
146                 if config.plugins.growlee.enable_incoming.value:
147                         self.serverPort = reactor.listenTCP(SNP_TCP_PORT, self.serverFactory)
148                         self.pending += 1
149
150         def gotIP(self, ip):
151                 self.clientPort = reactor.connectTCP(ip, SNP_TCP_PORT, self.clientFactory)
152                 self.pending += 1
153
154         def noIP(self, error):
155                 emergencyDisable()
156
157         def sendNotification(self, *args, **kwargs):
158                 self.clientFactory.sendNotification(*args, **kwargs)
159
160         def maybeClose(self, resOrFail, defer = None):
161                 self.pending -= 1
162                 if self.pending == 0:
163                         if defer:
164                                 defer.callback(True)
165
166         def stop(self):
167                 defer = Deferred()
168                 if self.clientPort:
169                         d = self.clientPort.disconnect()
170                         if d:
171                                 d.addBoth(self.maybeClose, defer = defer)
172                         else:
173                                 self.pending -= 1
174
175                 if self.serverPort:
176                         d = self.serverPort.stopListening()
177                         if d:
178                                 d.addBoth(self.maybeClose, defer = defer)
179                         else:
180                                 self.pending -= 1
181
182                 if self.pending == 0:
183                         reactor.callLater(1, defer.callback, True)
184                 return defer
185