Merge pull request #443 from Schrolli91/release_2_5

Release BW 2.5
This commit is contained in:
Bastian Schroll 2020-04-16 13:12:27 +02:00 committed by GitHub
commit 6054a38ff5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 878 additions and 533 deletions

View file

@ -1,13 +1,25 @@
# Changelog
### __[v2.5]__ - 16.04.2020
##### Added
- Divera-Plugin: Plugin zum Ansteuern der Divera-Api. [#415](https://github.com/Schrolli91/BOSWatch/pull/415)
- GPIO-Control: Plugin zum Ansteuern der GPIO Pins. [#438](https://github.com/Schrolli91/BOSWatch/pull/438)
##### Changed
- MySQL-Plugin: Index für die RIC Adresse hinzugefügt [#411](https://github.com/Schrolli91/BOSWatch/issues/411)
- MySQL-Plugin: INSERT Befehl für MySQL 8.x angepasst, Spaltennamen escaped [#410](https://github.com/Schrolli91/BOSWatch/issues/410)
- Pushover-Plugin: Konfigurationsmöglichkeit für den Sound [#418](https://github.com/Schrolli91/BOSWatch/issues/418)
##### Fixed
- Description-List: Buchstaben in FMS-Kennungen werden nun erkannt und zugelassen [#409](https://github.com/Schrolli91/BOSWatch/issues/409)
- MySQL-Plugin: Volle UTF-8 Kompatibilität für Datenbankstruktur, Verbindung und Darstellung im WebUI [#398](https://github.com/Schrolli91/BOSWatch/issues/398)
### __[v2.4.3]__ - 22.09.2019
##### Added
- Telegram-Plugin: In der generierten Übersichtkarte wird eine Anfahrtsroute integriert. Der Abfahrtsort ist konfiguierbar. [#382](https://github.com/Schrolli91/BOSWatch/pull/382)
- Hue-Plugin: Geräte die mit einer Hue bridge verbunden sind können aus BOSWatch ein- und ausgeschaltet werden. [#394](https://github.com/Schrolli91/BOSWatch/issues/394)
##### Changed
- FFAgent Plugin: zusätzliches OrderedDict "alarmHeadersOrdered" implementiert um das HTTP Header Ordering sicherzustellen. Zusätzlich den HTTP Request mittels Session implementiert um das Header Ordering zu bewahren. Zusätzliches Debug Logging für die Header implementiert. [#356] (https://github.com/Schrolli91/BOSWatch/issues/356)
- POC-Decoder: Im POC-Text wird nach einem RegEx, welcher Koordinaten enthält, gesucht. Werden diese gefunden, so stehen zwei neu befüllte Data-Felder Lon bzw Lat zur Verfügung.
- FFAgent Plugin: zusätzliches OrderedDict "alarmHeadersOrdered" implementiert um das HTTP Header Ordering sicherzustellen. Zusätzlich den HTTP Request mittels Session implementiert um das Header Ordering zu bewahren. Zusätzliches Debug Logging für die Header implementiert. [#356](https://github.com/Schrolli91/BOSWatch/issues/356)
- POC-Decoder: Im POC-Text wird nach einem RegEx, welcher Koordinaten enthält, gesucht. Werden diese gefunden, so stehen zwei neu befüllte Data-Felder Lon bzw Lat zur Verfügung. [#405](https://github.com/Schrolli91/BOSWatch/pull/405)
##### Fixed
- Asynchrone Alarme: Bei asynchroner Verarbeitung von schnell aufeinander folgenden Alarmen, wurde der Inhalt der Objekte typ, freq und data bereits vor dem Abschluss der Verarbeitung eines Alarms wieder überschrieben. Ergebnis hiervon war die Vermischung von RICs und Texten unterschiedlicher Alarme. Lösung über copy.deepcopy() [#394](https://github.com/Schrolli91/BOSWatch/issues/394)
- POC-Decoder: Bug wegen nicht zugeweisener Variable 'has_geo' [#410](https://github.com/Schrolli91/BOSWatch/issues/413) [HOTFIX]

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,10 @@ Simple Database Class (C) by Bastian Schroll
$this->error("Datenbank nicht gefunden!", mysqli_error($this->conn));
return false;
}
/* Set character set for database connection to utf8mb4 */
mysqli_query($this->conn, "SET NAMES 'utf8mb4'");
return true;
}

View file

@ -43,7 +43,7 @@ def loadCSV(typ, idField):
for row in reader:
logging.debug(row)
# only import rows with an integer as id, allow subrics though
if re.match("^[0-9]+[A-D]?$", row[idField], re.IGNORECASE):
if re.match("^[0-9A-F]+$", row[idField], re.IGNORECASE):
try:
resultList[row[idField].lower()] = stringConverter.convertToUTF8(row['description'])
except:
@ -102,7 +102,7 @@ def getDescription(typ, data):
@return: description as string
"""
resultStr = data;
resultStr = data
logging.debug("look up description lists")
try:
if typ == "FMS":

View file

@ -9,9 +9,9 @@ Global variables
"""
# version info
versionNr = "2.4.3"
versionNr = "2.5"
branch = "master"
buildDate = "22.09.2019"
buildDate = "16.04.2020"
# Global variables
config = 0

153
plugins/Divera/Divera.py Normal file
View file

@ -0,0 +1,153 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Divera-Plugin to send FMS-, ZVEI- and POCSAG - messages to Divera
@author: Marco Grosjohann
@requires: Divera-Configuration has to be set in the config.ini
"""
import logging # Global logger
import httplib # for the HTTP request
import urllib
from includes import globalVars # Global variables
# from includes.helper import timeHandler
from includes.helper import configHandler
from includes.helper import wildcardHandler
##
#
# onLoad (init) function of plugin
# will be called one time by the pluginLoader on start
#
def onLoad():
"""
While loading the plugins by pluginLoader.loadPlugins()
this onLoad() routine is called one time for initialize the plugin
@requires: nothing
@return: nothing
"""
# nothing to do for this plugin
return
##
#
# Main function of Divera-plugin
# will be called by the alarmHandler
#
def run(typ, freq, data):
"""
This function is the implementation of the Divera-Plugin.
It will send the data to Divera API
@type typ: string (FMS|ZVEI|POC)
@param typ: Typ of the dataset
@type data: map of data (structure see readme.md in plugin folder)
@param data: Contains the parameter
@type freq: string
@keyword freq: frequency of the SDR Stick
@requires: Divera-Configuration has to be set in the config.ini
@return: nothing
"""
try:
if configHandler.checkConfig("Divera"): # read and debug the config
if typ == "FMS":
#
# building message for FMS
#
text = globalVars.config.get("Divera", "fms_text")
title = globalVars.config.get("Divera", "fms_title")
priority = globalVars.config.get("Divera", "fms_prio")
elif typ == "ZVEI":
#
# building message for ZVEI
#
text = globalVars.config.get("Divera", "zvei_text")
title = globalVars.config.get("Divera", "zvei_title")
priority = globalVars.config.get("Divera","zvei_std_prio")
elif typ == "POC":
#
# building message for POC
#
if data["function"] == '1':
priority = globalVars.config.get("Divera", "SubA")
elif data["function"] == '2':
priority = globalVars.config.get("Divera", "SubB")
elif data["function"] == '3':
priority = globalVars.config.get("Divera", "SubC")
elif data["function"] == '4':
priority = globalVars.config.get("Divera", "SubD")
else:
priority = ''
text = globalVars.config.get("Divera", "poc_text")
title = globalVars.config.get("Divera", "poc_title")
else:
logging.warning("Invalid type: %s", typ)
return
try:
#
# Divera-Request
#
logging.debug("send Divera for %s", typ)
# replace the wildcards
text = wildcardHandler.replaceWildcards(text, data)
title = wildcardHandler.replaceWildcards(title, data)
# Logging data to send
logging.debug("Title : %s", title)
logging.debug("Text : %s", text)
logging.debug("Priority: %s", priority)
# check priority value
if (priority != 'false') and (priority != 'true'):
logging.info("No Priority set for type '%s'! Skipping Divera-Alarm!", typ)
return
# start the connection
conn = httplib.HTTPSConnection("www.divera247.com:443")
conn.request("GET", "/api/alarm",
urllib.urlencode({
"accesskey": globalVars.config.get("Divera", "accesskey"),
"title": title,
"text": text,
"priority": priority,
}))
except:
logging.error("cannot send Divera request")
logging.debug("cannot send Divera request", exc_info=True)
return
try:
#
# check Divera-Response
#
response = conn.getresponse()
if str(response.status) == "200": # Check Divera Response and print a Log or Error
logging.debug("Divera response: %s - %s", str(response.status), str(response.reason))
else:
logging.warning("Divera response: %s - %s", str(response.status), str(response.reason))
except: # otherwise
logging.error("cannot get Divera response")
logging.debug("cannot get Divera response", exc_info=True)
return
finally:
logging.debug("close Divera-Connection")
try:
request.close()
except:
pass
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)

View file

@ -91,7 +91,7 @@ def run(typ,freq,data):
# Connect to MySQL
#
logging.debug("connect to MySQL")
connection = mysql.connector.connect(host = globalVars.config.get("MySQL","dbserver"), port = globalVars.config.get("MySQL","dbport"), user = globalVars.config.get("MySQL","dbuser"), passwd = globalVars.config.get("MySQL","dbpassword"), db = globalVars.config.get("MySQL","database"), charset='utf8')
connection = mysql.connector.connect(host = globalVars.config.get("MySQL","dbserver"), port = globalVars.config.get("MySQL","dbport"), user = globalVars.config.get("MySQL","dbuser"), passwd = globalVars.config.get("MySQL","dbpassword"), db = globalVars.config.get("MySQL","database"), charset='utf8mb4')
cursor = connection.cursor()
except:
logging.error("cannot connect to MySQL")
@ -104,21 +104,21 @@ def run(typ,freq,data):
logging.debug("Insert %s", typ)
if typ == "FMS":
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableFMS")+" (time, fms, status, direction, directionText, tsi, description) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["fms"], data["status"], data["direction"], data["directionText"], data["tsi"], data["description"]))
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableFMS")+" (`time`, `fms`, `status`, `direction`, `directionText`, `tsi`, `description`) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["fms"], data["status"], data["direction"], data["directionText"], data["tsi"], data["description"]))
elif typ == "ZVEI":
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableZVEI")+" (time, zvei, description) VALUES (FROM_UNIXTIME(%s),%s,%s)", (data["timestamp"], data["zvei"], data["description"]))
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableZVEI")+" (`time`, `zvei`, `description`) VALUES (FROM_UNIXTIME(%s),%s,%s)", (data["timestamp"], data["zvei"], data["description"]))
elif typ == "POC":
if isSignal(data["ric"]):
if globalVars.config.getint("POC","netIdent_history"):
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (time,ric) VALUES (NOW(), '"+data["ric"]+"');")
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (`time`,`ric`) VALUES (NOW(), '"+data["ric"]+"');")
else:
cursor.execute("UPDATE "+globalVars.config.get("MySQL","tableSIG")+" SET time = NOW() WHERE ric = '"+data["ric"]+"';")
if cursor.rowcount == 0:
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (time,ric) VALUES (NOW(), '"+data["ric"]+"');")
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (`time`,`ric`) VALUES (NOW(), '"+data["ric"]+"');")
else:
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tablePOC")+" (time, ric, function, functionChar, msg, bitrate, description) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["ric"], data["function"], data["functionChar"], data["msg"], data["bitrate"], data["description"]))
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tablePOC")+" (`time`, `ric`, `function`, `functionChar`, `msg`, `bitrate`, `description`) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["ric"], data["function"], data["functionChar"], data["msg"], data["bitrate"], data["description"]))
else:
logging.warning("Invalid Typ: %s", typ)

View file

@ -17,7 +17,7 @@ SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40101 SET NAMES utf8mb4 */;
-- --------------------------------------------------------
@ -25,8 +25,8 @@ SET time_zone = "+00:00";
-- Datenbank anlegen `boswatch`
--
CREATE DATABASE IF NOT EXISTS boswatch;
USE boswatch;
CREATE DATABASE IF NOT EXISTS 'boswatch' DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE 'boswatch';
-- --------------------------------------------------------
@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS `bos_fms` (
`tsi` VARCHAR(3) NOT NULL,
`description` TEXT NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;
-- --------------------------------------------------------
@ -70,8 +70,9 @@ CREATE TABLE IF NOT EXISTS `bos_pocsag` (
`msg` TEXT NOT NULL,
`bitrate` INT(4) NOT NULL,
`description` TEXT NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
PRIMARY KEY (`ID`),
KEY `POCSAG_RIC_IDX` (`ric`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;
-- rename old columns including little error-prevention
#ALTER IGNORE TABLE `bos_pocsag` change `funktion` `function` INT(1);
@ -89,7 +90,7 @@ CREATE TABLE IF NOT EXISTS `bos_zvei` (
`zvei` VARCHAR(5) NOT NULL DEFAULT '0',
`description` TEXT NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;
-- --------------------------------------------------------
@ -102,7 +103,7 @@ CREATE TABLE IF NOT EXISTS `bos_signal` (
`time` DATETIME NOT NULL,
`ric` VARCHAR(7) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

View file

@ -128,6 +128,10 @@ def run(typ, freq, data):
# replace the wildcards
message = wildcardHandler.replaceWildcards(message, data)
title = wildcardHandler.replaceWildcards(title, data)
sound = globalVars.config.get("Pushover", "sound")
# set Default-Sound
if not sound:
sound = "pushover"
# start the connection
conn = httplib.HTTPSConnection("api.pushover.net:443")
@ -138,6 +142,7 @@ def run(typ, freq, data):
"message": message,
"html": globalVars.config.get("Pushover", "html"),
"title": title,
"sound": sound,
"priority": priority,
"retry": globalVars.config.get("Pushover", "retry"),
"expire": globalVars.config.get("Pushover", "expire")

View file

@ -0,0 +1,118 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author: KS
@requires: none
"""
# Imports
import RPi.GPIO as GPIO
import time
import threading
import logging # Global logger
from includes import globalVars # Global variables
# Helper function, uncomment to use
from includes.helper import timeHandler
from includes.helper import wildcardHandler
from includes.helper import configHandler
##
#
# onLoad (init) function of plugin
# will be called one time by the pluginLoader on start
#
def onLoad():
"""
While loading the plugins by pluginLoader.loadPlugins()
this onLoad() routine is called one time for initialize the plugin
@requires: nothing
@return: nothing
@exception: Exception if init has an fatal error so that the plugin couldn't work
"""
global GPIOPIN
global waitTime
GPIOPIN = globalVars.config.getint("gpiocontrol","pin")
waitTime = globalVars.config.getint("gpiocontrol","triggertime")
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(GPIOPIN, GPIO.OUT)
#GPIO schalten beim START
#GPIO.output(GPIOPIN, GPIO.LOW)
#time.sleep(1)
GPIO.output(GPIOPIN, GPIO.HIGH)
return
#
#
# Main function of plugin
# will be called by the alarmHandler
#
def run(typ,freq,data):
"""
@type typ: string (FMS|ZVEI|POC)
@param typ: Typ of the dataset
@type data: map of data (structure see readme.md in plugin folder)
@param data: Contains the parameter for dispatch
@type freq: string
@keyword freq: frequency of the SDR Stick
@requires: If necessary the configuration hast to be set in the config.ini.
@return: nothing
@exception: nothing, make sure this function will never thrown an exception
"""
try:
if configHandler.checkConfig("gpiocontrol"): #read and debug the config (let empty if no config used)
logging.debug(globalVars.config.get("gpiocontrol", "pin"))
logging.debug(globalVars.config.get("gpiocontrol", "triggertime"))
########## User Plugin CODE ##########
if typ == "FMS":
th = threading.Thread(target = trigger)
th.start()
#logging.warning("%s not supported", typ)
elif typ == "ZVEI":
th = threading.Thread(target = trigger)
th.start()
#logging.warning("%s not supported", typ)
elif typ == "POC":
if globalVars.config.get("gpiocontrol", "activerics") == "":
th = threading.Thread(target = trigger)
th.start()
else
if data["ric"] in globalVars.config.get("gpiocontrol", "activerics"):
th = threading.Thread(target = trigger)
th.start()
else:
logging.info("Ric not in activerics")
else:
logging.warning("Invalid Typ: %s", typ)
########## User Plugin CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)
def trigger():
GPIO.output(GPIOPIN, GPIO.LOW)
logging.info("GPIOPIN %s angeschaltet", GPIOPIN)
time.sleep(waitTime)
GPIO.output(GPIOPIN, GPIO.HIGH)
logging.info("GPIOPIN %s ausgeschaltet", GPIOPIN)
return