From b529e93763a29b5d19d2796df43a0db93309c2f7 Mon Sep 17 00:00:00 2001 From: JHCD Date: Tue, 28 Jul 2015 22:50:22 +0200 Subject: [PATCH] expand notifyMyAndroid-plugin for multi recipients and/or different priorities #33 use csv/nma.csv to config different priorities or multiple recipients --- config/config.template.ini | 5 + csv/nma.csv | 29 +++ includes/NMAHandler.py | 144 +++++++-------- includes/pynma/pynma.py | 8 + plugins/notifyMyAndroid/notifyMyAndroid.py | 204 ++++++++++++++++++--- 5 files changed, 291 insertions(+), 99 deletions(-) create mode 100644 csv/nma.csv mode change 100755 => 100644 includes/pynma/pynma.py diff --git a/config/config.template.ini b/config/config.template.ini index 7b61c50..b319042 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -270,6 +270,11 @@ priority = 0 # (f.e. if you use more than one instance of BOSWatch) appName = BOSWatch +# instead of a given APIKey/priority you could import them by a csv-file (0|1) +# APIKey and prioiry above will be ignored, if you use a csv +# configuration loaded from csv/nma.csv +usecsv = 0 + ##################### ##### Not ready yet # diff --git a/csv/nma.csv b/csv/nma.csv new file mode 100644 index 0000000..3640e4a --- /dev/null +++ b/csv/nma.csv @@ -0,0 +1,29 @@ +typ,id,APIKey,priority,comment +# +# BOSWatch CSV file for notifyMyAndroid-Plugin +# +# For each id (FMS, ZVEI, POC) you could set multiple APIKeys with different prioties +# Use the structure: typ, id, APIKey, priority, "comment" +# +# For more than one recipient you could you an id several times +# +# Typ: FMS|ZVEI|POC +# +# id: +# --- +# FMS: FMS Code +# ZVEI: ZVEI 5-tone Code +# POCSAG: POCSAG RIC + functionChar +# 1234567a = entry only for functionChar a +# 1234567* = entry for all functionChars +# +# Priority: goes from -2 (lowest) to 2 (highest). the default priority is 0 (normal) +# +# !!! DO NOT delete the first line !!! +# +POC,1000512*,123456789012345678901234567890123456789012345678,0,"Test for *" +POC,1000000a,123456789012345678901234567890123456789012345678,-2,"Priority-Test" +POC,1000000a,234567890123456789012345678901234567890123456789,-1,"Priority-Test" +POC,1000001b,123456789012345678901234567890123456789012345678,0,"Priority-Test" +POC,1000002c,123456789012345678901234567890123456789012345678,1,"Priority-Test" +POC,1000003d,123456789012345678901234567890123456789012345678,2,"Priority-Test" diff --git a/includes/NMAHandler.py b/includes/NMAHandler.py index 258a0d5..e0a7895 100644 --- a/includes/NMAHandler.py +++ b/includes/NMAHandler.py @@ -1,73 +1,73 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -""" -Logging Handler for NotifyMyAndroid - -@author: Jens Herrmann -""" - -import logging -from includes.pynma import pynma - -class NMAHandler(logging.Handler): # Inherit from logging.Handler - """ - Handler instances dispatch logging events to NotifyMyAndroid. - """ - - def __init__(self, APIKey, application="BOSWatch", event="Logging-Handler"): - """ - Initializes the handler with NMA-specific parameters. - - @param APIKey: might be a string containing 1 key or an array of keys - @param application: application name [256] - @param event: event name [1000] - """ - # run the regular Handler __init__ - logging.Handler.__init__(self) - # Our custom argument - self.APIKey = APIKey - self.application = application - self.event = event - self.nma = pynma.PyNMA(self.APIKey) - - - def emit(self, record): - """ - Send logging record via NMA - """ - # record.message is the log message - message = record.message - - # if exist, add details as NMA event: - # record.module is the module- or filename - if (len(record.module) > 0): - event = "Module: " + record.module - # record.functionName is the name of the function - # will be "" if the message is not in a function - if len(record.funcName) > 0: - if not record.funcName == "": - event += " - " + record.funcName + "()" - else: - # we have to set an event-text, use self.event now - event = self.event - - # record.levelno is the log level - # loglevel: 10 = debug => priority: -2 - # loglevel: 20 = info => priority: -1 - # loglevel: 30 = warning => priority: 0 - # loglevel: 40 = error => priority: 1 - # loglevel: 50 = critical => priority: 2 - if record.levelno >= 50: - priority = 2 - elif record.levelno >= 40: - priority = 1 - elif record.levelno >= 30: - priority = 0 - elif record.levelno >= 20: - priority = -1 - else: - priority = -2 - - # pynma.push(self, application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False) +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +""" +Logging Handler for NotifyMyAndroid + +@author: Jens Herrmann +""" + +import logging +from includes.pynma import pynma + +class NMAHandler(logging.Handler): # Inherit from logging.Handler + """ + Handler instances dispatch logging events to NotifyMyAndroid. + """ + + def __init__(self, APIKey, application="BOSWatch", event="Logging-Handler"): + """ + Initializes the handler with NMA-specific parameters. + + @param APIKey: might be a string containing 1 key or an array of keys + @param application: application name [256] + @param event: event name [1000] + """ + # run the regular Handler __init__ + logging.Handler.__init__(self) + # Our custom argument + self.APIKey = APIKey + self.application = application + self.event = event + self.nma = pynma.PyNMA(self.APIKey) + + + def emit(self, record): + """ + Send logging record via NMA + """ + # record.message is the log message + message = record.message + + # if exist, add details as NMA event: + # record.module is the module- or filename + if (len(record.module) > 0): + event = "Module: " + record.module + # record.functionName is the name of the function + # will be "" if the message is not in a function + if len(record.funcName) > 0: + if not record.funcName == "": + event += " - " + record.funcName + "()" + else: + # we have to set an event-text, use self.event now + event = self.event + + # record.levelno is the log level + # loglevel: 10 = debug => priority: -2 + # loglevel: 20 = info => priority: -1 + # loglevel: 30 = warning => priority: 0 + # loglevel: 40 = error => priority: 1 + # loglevel: 50 = critical => priority: 2 + if record.levelno >= 50: + priority = 2 + elif record.levelno >= 40: + priority = 1 + elif record.levelno >= 30: + priority = 0 + elif record.levelno >= 20: + priority = -1 + else: + priority = -2 + + # pynma.push(self, application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False) self.nma.push(application=self.application, event=event, description=message, priority=priority) \ No newline at end of file diff --git a/includes/pynma/pynma.py b/includes/pynma/pynma.py old mode 100755 new mode 100644 index 2fc5556..9f1d5d3 --- a/includes/pynma/pynma.py +++ b/includes/pynma/pynma.py @@ -68,6 +68,14 @@ takes 2 optional arguments: if type(developerkey) == str and len(developerkey) == 48: self._developerkey = developerkey + def pushWithAPIKey(self, apikey=[], application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False): + """Special Funktion""" + if apikey: + if type(apikey) == str: + apikey = [apikey] + self._apikey = uniq(apikey) + return self.push(application, event, description, url, contenttype, priority, batch_mode, html) + def push(self, application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False): """Pushes a message on the registered API keys. takes 5 arguments: diff --git a/plugins/notifyMyAndroid/notifyMyAndroid.py b/plugins/notifyMyAndroid/notifyMyAndroid.py index 6136143..e09a795 100644 --- a/plugins/notifyMyAndroid/notifyMyAndroid.py +++ b/plugins/notifyMyAndroid/notifyMyAndroid.py @@ -13,6 +13,8 @@ import logging # Global logger import socket # for connection import json # for data-transfer +import csv # for loading the APIKeys + from includes import globals # Global variables @@ -21,9 +23,51 @@ from includes.helper import timeHandler from includes.helper import uft8Converter # UTF-8 converter from includes.pynma import pynma + # local variables application = "BOSWatch" APIKey = None +remainingMsgs = None +usecsv = False +# data structures: xAPIKeyList[id][i] = (APIKey, priority) +fmsAPIKeyList = {} +zveiAPIKeyList = {} +pocAPIKeyList = {} + + +def checkResponse(response, APIKey): + """ + Helper function to check the response of NMA + + @type response: dict + @param response: Response of the pyNMA.push() method + @type data: string / array + @param data: a string containing 1 key or an array of keys + + @return: nothing + """ + # local variables + global remainingMsgs + try: + # + # check HTTP-Response + # + if str(response[APIKey]['code']) == "200": #Check HTTP Response an print a Log or Error + logging.debug("NMA response: %s" , str(response[APIKey]['code'])) + remainingMsgs = response[APIKey]['remaining'] + if int(remainingMsgs) == 0: + logging.error("NMA remaining msgs: %s" , str(remainingMsgs)) + if int(response[APIKey]['remaining']) < 20: + logging.warning("NMA remaining msgs: %s" , str(remainingMsgs)) + else: + logging.debug("NMA remaining msgs: %s" , str(remainingMsgs)) + else: + logging.warning("NMA response: %s - %s" , str(response[APIKey]['code']), str(response[APIKey]['message'])) + except: + logging.error("cannot read pynma response") + logging.debug("cannot read pynma response", exc_info=True) + return + ## # @@ -42,12 +86,74 @@ def onLoad(): # local variables global application global APIKey + global usecsv + global fmsAPIKeyList + global zveiAPIKeyList + global pocAPIKeyList # load config: configHandler.checkConfig("notifyMyAndroid") - APIKey = globals.config.get("notifyMyAndroid","APIKey") application = globals.config.get("notifyMyAndroid","appName") + usecsv = globals.config.getboolean("notifyMyAndroid","usecsv") + # if no csv should use, we take the APIKey directly + if usecsv == False: + APIKey = globals.config.get("notifyMyAndroid","APIKey") + else: + # import the csv-file + try: + logging.debug("-- loading nma.csv") + with open(globals.script_path+'/csv/nma.csv') as csvfile: + # DictReader expected structure described in first line of csv-file + reader = csv.DictReader(csvfile) + for row in reader: + logging.debug(row) + # only import rows with an supported types + supportedTypes = ["FMS", "ZVEI", "POC"] + if row['typ'] in supportedTypes: + try: + if "FMS" in row['typ']: + # if len for id in mainList raise an KeyErrorException, we have to init it... + try: + if len(fmsAPIKeyList[row['id']]) > 0: + pass + except KeyError: + fmsAPIKeyList[row['id']] = [] + # data structure: fmsAPIKeyList[fms][i] = (APIKey, priority) + fmsAPIKeyList[row['id']].append((row['APIKey'], row['priority'])) + + elif "ZVEI" in row['typ']: + # if len for id in mainList raise an KeyErrorException, we have to init it... + try: + if len(zveiAPIKeyList[row['id']]) > 0: + pass + except KeyError: + zveiAPIKeyList[row['id']] = [] + # data structure: zveiAPIKeyList[zvei][i] = (APIKey, priority) + zveiAPIKeyList[row['id']].append((row['APIKey'], row['priority'])) + + elif "POC" in row['typ']: + # if len for id in mainList raise an KeyErrorException, we have to init it... + try: + if len(pocAPIKeyList[row['id']]) > 0: + pass + except KeyError: + pocAPIKeyList[row['id']] = [] + # data structure: zveiAPIKeyList[ric][i] = (APIKey, priority) + pocAPIKeyList[row['id']].append((row['APIKey'], row['priority'])) + + except: + # skip entry in case of an exception + logging.debug("error in shifting...", exc_info=True) + pass + # if row['typ'] in supportedTypes + # for row in reader: + logging.debug("-- loading csv finished") + except: + logging.error("loading csvList for nma failed") + logging.debug("loading csvList for nma failed", exc_info=True) + raise + # and if usecsv == True return @@ -76,55 +182,99 @@ def run(typ,freq,data): # local variables global application global APIKey + global remainingMsgs + global usecsv + global fmsAPIKeyList + global zveiAPIKeyList + global pocAPIKeyList try: try: # # initialize to pyNMA # - nma = pynma.PyNMA(APIKey) - + nma = pynma.PyNMA() except: logging.error("cannot initialize pyNMA") - logging.debug("cannot initialize %s-socket", exc_info=True) + logging.debug("cannot initialize pyNMA", exc_info=True) # Without class, plugin couldn't work return - + else: # toDo is equals for all types, so only check if typ is supported supportedTypes = ["FMS", "ZVEI", "POC"] if typ in supportedTypes: logging.debug("Start %s to NMA", typ) try: - # send data - event = data['description'] + # build event and msg + event = uft8Converter.convertToUTF8(data['description']) msg = timeHandler.curtime() if len(data['msg']) > 0: msg += "\n" + data['msg'] - response = nma.push(application, uft8Converter.convertToUTF8(event), uft8Converter.convertToUTF8(msg), priority=globals.config.getint("notifyMyAndroid","priority")) + msg = uft8Converter.convertToUTF8(msg) + + # if not using csv-import, all is simple... + if usecsv == False: + response = nma.pushWithAPIKey(APIKey, application, event, msg, priority=globals.config.getint("notifyMyAndroid","priority")) + checkResponse(response, APIKey) + else: + if "FMS" in typ: + # lets look for fms in fmsAPIKeyList + xID = data['fms'] + try: + # data structure: fmsAPIKeyList[xID][i] = (xAPIKey, xPriority) + for i in range(len(fmsAPIKeyList[xID])): + (xAPIKey, xPriority) = fmsAPIKeyList[xID][i] + response = nma.pushWithAPIKey(xAPIKey, application, event, msg, priority=xPriority) + checkResponse(response, xAPIKey) + except KeyError: + # nothing found + pass + + elif "ZVEI" in typ: + # lets look for zvei in zveiAPIKeyList + xID = data['zvei'] + try: + # data structure: zveiAPIKeyList[xID][i] = (xAPIKey, xPriority) + for i in range(len(zveiAPIKeyList[xID])): + (xAPIKey, xPriority) = zveiAPIKeyList[xID][i] + response = nma.pushWithAPIKey(xAPIKey, application, event, msg, priority=xPriority) + checkResponse(response, xAPIKey) + except KeyError: + # nothing found + pass + + elif "POC" in typ: + xID = "" + # 1. lets look for ric+functionChar in pocAPIKeyList + try: + xID = data['ric'] + data['functionChar'] + # data structure: pocAPIKeyList[xID][i] = (xAPIKey, xPriority) + for i in range(len(pocAPIKeyList[xID])): + (xAPIKey, xPriority) = pocAPIKeyList[xID][i] + response = nma.pushWithAPIKey(xAPIKey, application, event, msg, priority=xPriority) + checkResponse(response, xAPIKey) + except KeyError: + # nothing found + pass + # 2. lets look for ric* in pocAPIKeyList + try: + xID = data['ric'] + "*" + # data structure: pocAPIKeyList[xID][i] = (xAPIKey, xPriority) + for i in range(len(pocAPIKeyList[xID])): + (xAPIKey, xPriority) = pocAPIKeyList[xID][i] + logging.debug("-- found %s, %s", xAPIKey, xPriority) + response = nma.pushWithAPIKey(xAPIKey, application, event, msg, priority=xPriority) + checkResponse(response, xAPIKey) + except KeyError: + # nothing found + pass + # end if "POC" in typ + # end if usecsv == True except: logging.error("%s to NMA failed", typ) logging.debug("%s to NMA failed", typ, exc_info=True) return - else: - try: - # - # check HTTP-Response - # - if str(response[APIKey]['code']) == "200": #Check HTTP Response an print a Log or Error - logging.debug("NMA response: %s" , str(response[APIKey]['code'])) - if int(response[APIKey]['remaining']) == 0: - logging.error("NMA remaining msgs: %s" , str(response[APIKey]['remaining'])) - if int(response[APIKey]['remaining']) < 20: - logging.warning("NMA remaining msgs: %s" , str(response[APIKey]['remaining'])) - else: - logging.debug("NMA remaining msgs: %s" , str(response[APIKey]['remaining'])) - else: - logging.warning("NMA response: %s - %s" , str(response[APIKey]['code']), str(response[APIKey]['message'])) - except: #otherwise - logging.error("cannot read pynma response") - logging.debug("cannot read pynma response", exc_info=True) - return else: logging.warning("Invalid Typ: %s", typ)