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 twisted.web.client import getPage
-from struct import unpack
-from socket import gaierror
-from urllib import urlencode
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, ConfigSelection, ConfigSet
+ ConfigText, ConfigPassword, ConfigYesNo, ConfigSelection, ConfigSet, \
+ ConfigSubList, ConfigNumber, NoSave
from Components.ConfigList import ConfigListScreen
from Components.Sources.StaticText import StaticText
-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()
-config.plugins.growlee.prowl_api_key = ConfigText(fixed_size=False)
-config.plugins.growlee.protocol = ConfigSelection(default="growl", choices = [("growl", "Growl"), ("snarl", "Snarl"), ("prowl", "Prowl")])
-config.plugins.growlee.blacklist = ConfigSet(choices = [])
-
-NOTIFICATIONID = 'GrowleeReceivedNotification'
+from GrowleeConnection import gotNotification, emergencyDisable, growleeConnection
+
+from . import NOTIFICATIONID
+
+growlee = ConfigSubsection()
+config.plugins.growlee = growlee
+growlee.hostcount = ConfigNumber(default=0)
+growlee.hosts = ConfigSubList()
+
+def addHost(name):
+ s = ConfigSubsection()
+ s.name = ConfigText(default=name, fixed_size=False)
+ s.enable_incoming = ConfigYesNo(default=False)
+ s.enable_outgoing = ConfigYesNo(default=False)
+ s.address = ConfigText(fixed_size=False)
+ s.password = ConfigPassword()
+ s.protocol = ConfigSelection(default="growl", choices=[("growl", "Growl"), ("snarl", "Snarl"), ("prowl", "Prowl"), ("syslog", "Syslog UDP")])
+ s.level = ConfigSelection(default="-1", choices=[("-1", _("Low (Yes/No)")), ("0", _("Normal (Information)")), ("1", _("High (Warning)")), ("2", _("Highest (Emergency)"))])
+ s.blacklist = ConfigSet(choices=[])
+ config.plugins.growlee.hosts.append(s)
+ return s
+
+i = 0
+while i < growlee.hostcount.value:
+ addHost(str(i+1))
+ i += 1
+
+# XXX: change to new config format
+# NOTE: after some time, remove this and hardcode default length to 1
+# since internally we assume to have at least 1 host configured
+if growlee.hostcount.value == 0:
+ growlee.enable_outgoing = ConfigYesNo(default=False)
+ growlee.enable_incoming = ConfigYesNo(default=False)
+ growlee.address = ConfigText(fixed_size=False)
+ growlee.password = ConfigPassword()
+ password = growlee.password.value
+ growlee.prowl_api_key = ConfigText()
+ growlee.protocol = ConfigSelection(default="growl", choices=[("growl", "Growl"), ("snarl", "Snarl"), ("prowl", "Prowl")])
+ growlee.level = ConfigSelection(default="-1", choices=[("-1", _("Low (Yes/No)")), ("0", _("Normal (Information)")), ("1", _("High (Warning)")), ("2", _("Highest (Emergency)"))])
+ growlee.blacklist = ConfigSet(choices=[])
+ if growlee.protocol.value == "prowl":
+ password = growlee.prowl_api_key.value
+
+ s = addHost(_("Converted connection"))
+ s.enable_incoming.value = growlee.enable_incoming.value
+ s.enable_outgoing.value = growlee.enable_outgoing.value
+ s.address.value = growlee.address.value
+ s.password.value = password
+ s.protocol.value = growlee.protocol.value
+ s.level.value = growlee.level.value
+ s.blacklist.value = growlee.blacklist.value
+
+ growlee.enable_incoming.value = False
+ growlee.enable_outgoing.value = False
+ growlee.address.value = ""
+ growlee.password.value = ""
+ growlee.prowl_api_key.value = ""
+ growlee.protocol.value = "growl"
+ growlee.level.value = "-1"
+ growlee.blacklist.value = []
+
+ growlee.hostcount.value += 1
+ growlee.save()
+ del s
+
+del i, growlee
class GrowleeConfiguration(Screen, ConfigListScreen):
+ skin = """
+ <screen name="RSSSetup" position="center,center" size="560,400" title="Simple RSS Reader Setup" >
+ <ePixmap position="0,0" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+ <ePixmap position="140,0" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+ <ePixmap position="280,0" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+ <ePixmap position="420,0" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
+ <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+ <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+ <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+ <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+ <widget name="config" position="5,45" size="550,350" scrollbarMode="showOnDemand" />
+ </screen>"""
+
def __init__(self, session):
Screen.__init__(self, session)
- self.skinName = [ "GrowleeConfiguration", "Setup" ]
# Buttons
self["key_red"] = StaticText(_("Cancel"))
self["key_green"] = StaticText(_("OK"))
+ self["key_yellow"] = StaticText(_("New"))
+ self["key_blue"] = StaticText(_("Delete"))
# Summary
self.setup_title = "Growlee Configuration"
self.onChangedEntry = []
# Define Actions
- self["actions"] = ActionMap(["SetupActions"],
- {
- "cancel": self.keyCancel,
- "save": self.keySave,
- }
- )
-
- config.plugins.growlee.protocol.addNotifier(self.setupList, initial_call=False)
+ self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
+ {
+ "blue": self.delete,
+ "yellow": self.new,
+ "cancel": self.keyCancel,
+ "save": self.keySave,
+ })
+
+ self.hostElement = NoSave(ConfigSelection(choices=[(x, x.name.value) for x in config.plugins.growlee.hosts]))
+ self.hostElement.addNotifier(self.setupList, initial_call=False)
ConfigListScreen.__init__(
self,
[],
session=session,
on_change=self.changed
)
+ self.cur = self.hostElement.value
# Trigger change
self.setupList()
self.changed()
+ def delete(self):
+ from Screens.MessageBox import MessageBox
+
+ self.session.openWithCallback(
+ self.deleteConfirm,
+ MessageBox,
+ _("Really delete this entry?\nIt cannot be recovered!")
+ )
+
+ def deleteConfirm(self, result):
+ if result and config.plugins.growlee.hostcount.value > 0:
+ config.plugins.growlee.hostcount.value -= 1
+ config.plugins.growlee.hosts.remove(self.cur)
+ self.hostElement.setChoices([(x, x.name.value) for x in config.plugins.growlee.hosts])
+ self.cur = self.hostElement.value
+
+ def new(self):
+ self.cur = addHost(_("New connection"))
+ config.plugins.growlee.hostcount.value += 1
+ self.hostElement.setChoices([(x, x.name.value) for x in config.plugins.growlee.hosts])
+ self.hostElement.setValue(self.cur)
+
def changed(self):
for x in self.onChangedEntry:
x()
def setupList(self, *args):
+ last = self.cur
+ if self.setupList in last.protocol.notifiers:
+ last.protocol.notifiers.remove(self.setupList)
+ cur = self.hostElement.value
+ self.cur = cur
+ cur.protocol.notifiers.append(self.setupList)
+
l = [
- getConfigListEntry(_("Type"), config.plugins.growlee.protocol),
- getConfigListEntry(_("Send Notifications?"), config.plugins.growlee.enable_outgoing),
+ getConfigListEntry(_("Host"), self.hostElement),
+ getConfigListEntry(_("Name"), cur.name),
+ getConfigListEntry(_("Type"), cur.protocol),
+ getConfigListEntry(_("Minimum Priority"), cur.level),
+ getConfigListEntry(_("Send Notifications?"), cur.enable_outgoing),
]
- if config.plugins.growlee.protocol.value == "prowl":
- l.append(getConfigListEntry(_("API Key"), config.plugins.growlee.prowl_api_key))
+ proto = cur.protocol.value
+ if proto == "prowl":
+ l.append(getConfigListEntry(_("API Key"), cur.password))
else:
l.extend((
- getConfigListEntry(_("Receive Notifications?"), config.plugins.growlee.enable_incoming),
- getConfigListEntry(_("Address"), config.plugins.growlee.address),
- getConfigListEntry(_("Password"), config.plugins.growlee.password),
+ getConfigListEntry(_("Receive Notifications?"), cur.enable_incoming),
+ getConfigListEntry(_("Address"), cur.address),
))
+ if proto == "growl":
+ l.append(
+ getConfigListEntry(_("Password"), cur.password)
+ )
self["config"].list = l
def createSummary(self):
return SetupSummary
+ def cancelConfirm(self):
+ ConfigListScreen.cancelConfirm(self)
+ config.plugins.growlee.cancel()
+
def keySave(self):
+ config.plugins.growlee.save()
if self["config"].isChanged():
- global port
- if port:
- def maybeConnect(*args, **kwargs):
- if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
- doConnect()
-
- d = port.stopListening()
- if d is not None:
- d.addCallback(maybeConnect).addErrback(emergencyDisable)
- else:
- maybeConnect()
- elif config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
- doConnect()
+ def doConnect(*args, **kwargs):
+ growleeConnection.listen()
+
+ d = growleeConnection.stop()
+ if d is not None:
+ d.addCallback(doConnect).addErrback(emergencyDisable)
+ else:
+ maybeConnect()
self.saveAll()
self.close()
def close(self):
- config.plugins.growlee.protocol.notifiers.remove(self.setupList)
+ if self.setupList in self.cur.protocol.notifiers:
+ self.cur.protocol.notifiers.remove(self.setupList)
Screen.close(self)
def configuration(session, **kwargs):
session.open(GrowleeConfiguration)
-def doConnect():
- global port
- if config.plugins.growlee.protocol.value == "snarl":
- port = reactor.listenTCP(GROWL_UDP_PORT, growlProtocolOneWrapper)
- else:
- port = reactor.listenUDP(GROWL_UDP_PORT, growlProtocolOneWrapper)
-
-def emergencyDisable(*args, **kwargs):
- global port
- if port:
- port.stopListening()
- port = None
-
- if gotNotification in Notifications.notificationAdded:
- Notifications.notificationAdded.remove(gotNotification)
- Notifications.AddPopup(
- _("Network error.\nDisabling Growlee!"),
- MessageBox.TYPE_ERROR,
- 10
- )
-
-class GrowlProtocolOneWrapper(DatagramProtocol):
- def startProtocol(self):
- proto = config.plugins.growlee.protocol.value
- if config.plugins.growlee.enable_outgoing.value and not proto == "prowl":
- addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
- if proto == "growl":
- p = GrowlRegistrationPacket(password=config.plugins.growlee.password.value)
- p.addNotification()
- payload = p.payload()
- else: #proto == "snarl":
- payload = "type=SNP#?version=1.0#?action=register#?app=growlee\r\n"
- try:
- self.transport.write(payload, addr)
- except gaierror:
- emergencyDisable()
-
- def doStop(self):
- if config.plugins.growlee.enable_outgoing.value and config.plugins.growlee.protocol.value == "snarl":
- addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
- payload = "type=SNP#?version=1.0#?action=unregister#?app=growlee\r\n"
- try:
- self.transport.write(payload, addr)
- except gaierror:
- pass
- DaragramProtocol.doStop(self)
-
- def sendNotification(self, *args, **kwargs):
- if not self.transport or not config.plugins.growlee.enable_outgoing.value:
- return
-
- proto = config.plugins.growlee.protocol.value
- if proto == "prowl":
- headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}
- data = {
- 'apikey': config.plugins.growlee.prowl_api_key.value,
- 'application': "growlee",
- 'event': kwargs.get('title', 'No title.'),
- 'description': kwargs.get('description', 'No message.'),
- 'priority': kwargs.get('priority', 0),
- }
-
- getPage('https://prowl.weks.net/publicapi/add/', method = 'POST', headers = headers, postdata = urlencode(data)).addErrback(emergencyDisable)
- else:
- addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
- if proto == "growl":
- # map timeout -> sticky
- if "timeout" in kwargs:
- if kwargs["timeout"] == -1:
- kwargs["sticky"] = True
- del kwargs["timeout"]
-
- p = GrowlNotificationPacket(*args, application="growlee", **kwargs)
- payload = p.payload()
- else: #proto == "snarl":
- title = kwargs.get('title', 'No title.')
- text = kwargs.get('description', 'No message.')
- timeout = kwargs.get('timeout', -1)
-
- # NOTE: timeout = 0 means sticky, so add one second to map -1 to 0 and make 0 non-sticky
- if timeout < 2:
- timeout += 1
-
- payload = "type=SNP#?version=1.0#?action=notification#?app=growlee#?class=growleeClass#?title=%s#?text=%s#?timeout=%d\r\n" % (title, text, timeout)
- try:
- self.transport.write(payload, addr)
- except gaierror:
- emergencyDisable()
-
- def datagramReceived(self, data, addr):
- proto = config.plugins.growlee.protocol.value
- if proto == "prowl" or not config.plugins.growlee.enable_incoming.value:
- return
-
- Len = len(data)
- if proto == "growl":
- if Len < 16:
- 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])
-
- Notifications.AddNotificationWithID(
- NOTIFICATIONID,
- MessageBox,
- text = title + '\n' + description,
- type = MessageBox.TYPE_INFO,
- timeout = 5,
- close_on_any_key = True,
- )
-
- # TODO: do we want to handle register packets? :-)
- else: #proto == "snarl":
- if Len < 23 or not data[:23] == "type=SNP#?version=1.0#?":
- return
-
- items = data[23:].split('#?')
-
- title = ''
- description = ''
- timeout = 5
- for item in items:
- key, value = item.split('=')
- if key == "action":
- if value != "notification":
- # NOTE: we pretend to handle&accept pretty much everything one throws at us
- addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
- payload = "SNP/1.0/0/OK\r\n"
- try:
- self.transport.write(payload, addr)
- except gaierror:
- emergencyDisable()
- return
- elif key == "title":
- title = value
- elif key == "text":
- description = value
- elif key == "timeout":
- timeout = int(value)
-
- Notifications.AddNotificationWithID(
- NOTIFICATIONID,
- MessageBox,
- text = title + '\n' + description,
- type = MessageBox.TYPE_INFO,
- timeout = timeout,
- close_on_any_key = True,
- )
-
- # return ok
- addr = (config.plugins.growlee.address.value, GROWL_UDP_PORT)
- payload = "SNP/1.0/0/OK\r\n"
- try:
- self.transport.write(payload, addr)
- except gaierror:
- emergencyDisable()
-
-
-growlProtocolOneWrapper = GrowlProtocolOneWrapper()
-port = None
-
-def gotNotification():
- notifications = Notifications.notifications
- if notifications:
- _, screen, args, kwargs, id = notifications[-1]
- if screen is MessageBox and id != NOTIFICATIONID and id not in config.plugins.growlee.blacklist.value:
-
- type = kwargs.get("type", 0)
- timeout = kwargs.get("timeout", -1)
-
- if "text" in kwargs:
- description = kwargs["text"]
- else:
- description = args[0]
- description = description.decode('utf-8')
-
- # NOTE: priority is in [-2; 2] but type is [0; 3] so map it
- # XXX: maybe priority==type-2 would be more appropriate
- growlProtocolOneWrapper.sendNotification(title="Dreambox", description=description, password=config.plugins.growlee.password.value, priority=type-1, timeout=timeout)
-
def autostart(**kwargs):
- if config.plugins.growlee.enable_incoming.value or config.plugins.growlee.enable_outgoing.value:
- doConnect()
-
# 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)
+ growleeConnection.listen()
+
def Plugins(**kwargs):
return [
PluginDescriptor(