Compare commits

..

No commits in common. "master" and "v2.2-beta" have entirely different histories.

71 changed files with 1950 additions and 2571 deletions

12
.github/FUNDING.yml vendored
View file

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: [Schrolli91] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

8
.gitignore vendored
View file

@ -3,11 +3,3 @@
*.log *.log
config.ini config.ini
log/ log/
\.project
\.pydevproject
\.settings/
\.idea/

View file

@ -5,6 +5,7 @@ sudo: required
branches: branches:
only: only:
- master - master
- beta
- develop - develop
before_script: before_script:

View file

@ -1,135 +0,0 @@
# Changelog
### __[v2.5.2]__ - 08.01.2021
##### Added
- fhemCmd-Plugin: New plugin fhemCmd to execute commands in FHEM home automation. [#457](https://github.com/Schrolli91/BOSWatch/pull/457)
- Add field "ricFuncChar" in data structure for POC messages as a combination of "ric" and "functionChar" for using in RegEx filter. [#459](https://github.com/Schrolli91/BOSWatch/pull/459)
- MQTT Plugin: Send alarms to an MQTT broker [#498](https://github.com/Schrolli91/BOSWatch/pull/498)
##### Changed
- Divera Plugin: Add individual alarms for FMS, ZVEI and POC. [#451](https://github.com/Schrolli91/BOSWatch/pull/451)
- install.sh: local git repo available at /opt/boswatch (or at your own path). Updates easier with `git pull` in /opt/boswatch. [#452](https://github.com/Schrolli91/BOSWatch/pull/452)
- Telegram Plugin: Add support for simple HTML style message formatting. [#482](https://github.com/Schrolli91/BOSWatch/pull/482)
##### Fixed
- install.sh: old version of mysql-connector-python removed; add new via pip [#452](https://github.com/Schrolli91/BOSWatch/pull/452) [#445](https://github.com/Schrolli91/BOSWatch/issues/445)
- GPIOcontrol plugin: Fixing indentation errors. [#465](https://github.com/Schrolli91/BOSWatch/pull/465)
### __[v2.5.1]__ - 28.04.2020
##### Added
- Plugin requirements: Added requirements.txt for all plugins requiring extra python packages so the install will be easier [#446](https://github.com/Schrolli91/BOSWatch/pull/446)
- DescriptionList POC: add new description parameter for Sub-RICs without a main RIC definition. parameter 'onlysubric'. [#449](https://github.com/Schrolli91/BOSWatch/pull/449)
##### Fixed
- MySQL plugin: Ensure character set (utf8mb4) and collation (utf8mb4_general_ci) are set correctly when connection to database is established. [#447](https://github.com/Schrolli91/BOSWatch/pull/447)
- E-Mail plugin: Create MIME-compliant header that can contain any kind of string. [#448](https://github.com/Schrolli91/BOSWatch/pull/448)
### __[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. [#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]
### __[v2.4.2]__ - 11.03.2019
##### Added
- Telegram-Plugin: In der generierten Übersichtkarte wird eine Anfahrtsroute integriert. Der Abfahrtsort ist konfiguierbar. [#382](https://github.com/Schrolli91/BOSWatch/pull/382)
##### Changed
- Telegram-Plugin: Aufrufe der Google API erfolgen per SSL und ohne zusätzliche Bibliotheken [#382](https://github.com/Schrolli91/BOSWatch/pull/382)
### __[v2.4.1]__ - 23.10.2018
##### Added
- Pushover-Plugin: Priorität für einzelne RIC und ZVEI in config einstellbar [#378](https://github.com/Schrolli91/BOSWatch/pull/378)
##### Changed
- Kleinere Anpassungen im Telegram Plugin (Karten-Generierung) [#380](https://github.com/Schrolli91/BOSWatch/pull/380)
##### Removed
- Notify-my-Andoird Plugin und Logging-Handler wegen Einstellung des Service entfernt [#374](https://github.com/Schrolli91/BOSWatch/pull/374)
### __[v2.4]__ - 17.08.2018
##### Added
- Config Eintrag um Port für MySQL Plugin festzulegen [#345](https://github.com/Schrolli91/BOSWatch/pull/345)
- FMS und ZVEI Support für Pushover Plugin [#352](https://github.com/Schrolli91/BOSWatch/pull/352)
- Benutzerdefinierte Nachrichten für Pushover Plugin in config [#352](https://github.com/Schrolli91/BOSWatch/pull/352)
##### Changed
- multicastAlarm Plugin - RICs die von multicastAlarm genutzt werden, müssen nicht mehr in der config bei allow_ric bzw. filter_range_start/filter_range_end berücksichtigt werden. [#357](https://github.com/Schrolli91/BOSWatch/pull/357)
- FFAgent Plugin - Debug Logging für die alarmHeaders eingebaut zwecks Troubleshooting [#354](https://github.com/Schrolli91/BOSWatch/pull/354)
- multicastAlarm Plugin - Buffer nach jedem Alarm löschen - erlaubt in kombination mit "doubleFilter_check_msg" die Verwendung in Netzen, die zwischen multicastAlarm RICs auch normale Alarme senden. [#370](https://github.com/Schrolli91/BOSWatch/pull/370)
##### Fixed
- Fehler beim Auslesen der netIdent_RIC im MySQL Plugin [#347](https://github.com/Schrolli91/BOSWatch/pull/347)
- FFAgent Plugin - Typo bei alarmHeaders für Live Betrieb gefixt [#354](https://github.com/Schrolli91/BOSWatch/pull/354)
### __[v2.3]__ - 22.12.2017
##### Added
- zuschaltbare POCSAG Multicast-Alarm Funktionalität [#307](https://github.com/Schrolli91/BOSWatch/pull/307)
- Flag in Config um nur letzte Net Ident oder gesamte Historie zu speichern [#317](https://github.com/Schrolli91/BOSWatch/pull/317)
##### Removed
- Beta Branch aus Readme, Installer und Travis-CI entfernt [#324](https://github.com/Schrolli91/BOSWatch/pull/324)
##### Fixed
- Bug in httpRequest Plugin (data Field wurde überschrieben) [#337](https://github.com/Schrolli91/BOSWatch/pull/337)
- Kommentar für FirEmergency Einstellung angepasst [#338](https://github.com/Schrolli91/BOSWatch/pull/338)
### __[v2.2.2]__ - 21.10.2017
##### Added
- Installations Script für Services [#316](https://github.com/Schrolli91/BOSWatch/pull/316)
##### Changed
- Telegram Plugin importiert Google Maps Funktionen nur noch wenn API Key eingetragen ist [#315](https://github.com/Schrolli91/BOSWatch/pull/315)
- Versions Nummer und Branch Name getrennt [3fed1ac](https://github.com/Schrolli91/BOSWatch/commit/3fed1ac12af8690213766e0e81d71c237530ed2c)
##### Deprecated
- Beta Branch wird mit nächstem Update entfernt [Forum](http://boswatch.de/index.php?thread/16-beta-branch-abschaffen/&postID=113#post113)
##### Fixed
- Schreibfehler der Pfadangabe im Installer [#317](https://github.com/Schrolli91/BOSWatch/pull/317)
- Schreibfehler in Service Readme [#313](https://github.com/Schrolli91/BOSWatch/issues/313)
- Einige Code-Style Verbesserungen [#310](https://github.com/Schrolli91/BOSWatch/pull/310)
### __[v2.2.1]__ - 19.09.2017
##### Added
- Neues Service Script [#263](https://github.com/Schrolli91/BOSWatch/pull/263)
- Eigene Message für jeden Typ im Telegram Plugin in der config definierbar [#267](https://github.com/Schrolli91/BOSWatch/pull/267)
- httpRequest Plugin unterstützt nun mehrere URLs [254](https://github.com/Schrolli91/BOSWatch/pull/254)
##### Changed
- Name der csv Dateien geändert um überschreiben bei Update zu vermeiden [#262](https://github.com/Schrolli91/BOSWatch/pull/262)
- Description Liste kann nun zusätzlich Einträge für jede Subric enthalten (POCSAG) [#271](https://github.com/Schrolli91/BOSWatch/pull/271)
- RegEX verbietet nun grundsätzlich alles - Es muss explizit zugelassen werden (wenn RegEX aktiv) [#284](https://github.com/Schrolli91/BOSWatch/pull/284)
##### Fixed
- Bug im SMS77 Plugin behoben [#257](https://github.com/Schrolli91/BOSWatch/issues/257)
- einige Code-Style Verbesserungen
----------------------------
Zum schreiben des Changelog's siehe:
http://keepachangelog.com/de/1.0.0/
### __[v#.#]__ - unreleased
##### Added
##### Changed
##### Deprecated
##### Removed
##### Fixed
##### Security

118
Konzept.md Normal file
View file

@ -0,0 +1,118 @@
# BOSWatch 3.0
============
Verpacken der Funktionalitäten in Klassen um OOP-Grundsätze zu erreichen.
## Dekodierung und Auswertung trennen.
### Client:
- reine Dekodierung mittels rtl-fm und multimon
- Keine Filter usw. nur die Dekoder, Daten verpacken, verschicken
- per TCP Socket an den Server
### Server:
- Empfängt die TCP Socket Pakete der einzelnen Clients
- Durch doubleFiltering fallen doppelt eingehende Alarme der Clienten sowieso raus
- Danach Filterung usw. dann call an die plugins
## Konfiguration:
- Alle Einstellungen in INI File
- Einziges Argument beim Start des Clienten ist der Name der INI (-v -q -t sollen auch bleiben)
- So werden mehrere Sticks auf einem Rechner einfach möglich ohne BOSWatch Ordner kopieren zu müssen
### Client:
```
[Server]
IP = 127.0.0.1
PORT = 23
[Client]
Name = BOSWatch Client 1
LogDir = log/
[Stick]
device = 0
Frequency = 85...M
PPMError = 0
Squelch = 0
gain = 100
[Decoder]
FMS = 0
ZVEI = 0
POC512 = 0
POC1200 = 1
POC2400 = 0
```
### Server:
```
[Server]
PORT = 23
[Filter]
...
[Plugins]
MySQL = 1
template = 0
...
```
### Plugin:
- Konfigurations Datei für Plugin mit in den Plugin Ordner
- Plugin läd bei Bedarf seine Config selbst, die geht BOSWatch ja nichts an
- Aktuell wird eine ewig lange Config geladen, obwohl 90% der Plugins nicht genutzt werden
## Filterung
Ein Vernünftiges Filterkonzept sollte aufgestellt werden, welches bei POC, FMS und ZVEI gleichermaßen funktioniert
und daher nicht 3 mal implementiert erden muss.
## Versions Überprüfung
über die LIB sched.py - https://docs.python.org/3/library/sched.html - können Zeitgesteuerte Events gestartet werden.
Dies kann zur Überprüfung einer neuen Software version verwendet werden.
information des Nutzers muss noch überlegt werden - evtl als "Alarm" absetzen über normalen Plugin weg.
## Code Dokumentation
Dokumentiert werden sollten alle Funktion und Klassen in Doxygen gerechter Notation.
Genaue Erklärung und Bennenung der Tags in der Doxygen Hilfe
```
class Hello:
## @brief Short description.
# Longer description.
#
# @param self
# @param name Another Parameter
# @return value Returns a Value
def __init__(self, name):
## @brief Constructor
# Longer description optinal.
#
# @param self
# @param name Another Parameter
dosomething(12)
def dosomething(self, x):
## @brief Do something
# Longer description for do something.
#
# @param self
# @param x Another Parameter
# @return value Returns a 0
dosomethingelse
return 0
```

109
README.md
View file

@ -1,43 +1,32 @@
|Branch|Code Qualität|CI-Build|
|---|---|---|
|master|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/master.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=3763821)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=master)](https://travis-ci.org/Schrolli91/BOSWatch)|
|beta|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/beta.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=4213030)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=beta)](https://travis-ci.org/Schrolli91/BOSWatch)|
|develop|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/develop.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=3763820)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=develop)](https://travis-ci.org/Schrolli91/BOSWatch)|
**Unterstützung gesucht**
Zur Weiterentwicklung des Programms benötigen wir Deine Mithilfe - bitte melde dich per Issue, wenn du Anwender in einem verschlüsselten POCSAG-Netz und im (legalen) Besitz des dazugehörigen Schlüssels bist.
In der Zukunft wollen wir die Möglichkeit schaffen, codierte Nachrichten zu entschlüsseln (und nur dann, wenn der Schlüssel bekannt ist!), dafür brauchen wir Dich als Tester!
**Readme ist veraltet** - bitte im [Wiki](https://github.com/Schrolli91/BOSWatch/wiki) nachschauen!
![# BOSWatch](/boswatch.png) ![# BOSWatch](/boswatch.png)
:satellite: Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG :satellite: :satellite: Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG :satellite:
#### WICHTIG
**Es wird darauf hingewiesen, dass für die Teilnahme am BOS-Funk nur nach den Technischen Richtlinien der BOS zugelassene Funkanlagen verwendet werden dürfen.**
**Der BOS-Funk ist ein nichtöffentlicher mobiler Landfunk. Privatpersonen gehören nicht zum Kreis der berechtigten Funkteilnehmer.** _(Quelle: TR-BOS)_
#### Notice: #### Notice:
The intercept of the German BOS radio is **strictly prohibited** and will be prosecuted. the use is **only authorized** personnel permitted. The intercept of the German BOS radio is **strictly prohibited** and will be prosecuted. the use is **only authorized** personnel permitted.
The software was developed using the Multimon-NG code, a function in the real operation can not be guaranteed. The software was developed using the Multimon-NG code, a function in the real operation can not be guaranteed.
### Fast support on https://bwcc.boswatch.de (Mattermost)
### BW3 in first testing phase - see: https://github.com/BOSWatch/BW3-Core
|Branch|Code Qualität|CI-Build|
|---|---|---|
|master|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/master.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=3763821)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=master)](https://travis-ci.org/Schrolli91/BOSWatch)|
|develop|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/develop.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=3763820)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=develop)](https://travis-ci.org/Schrolli91/BOSWatch)|
**Unterstützung gesucht**
Zur Weiterentwicklung des Programms benötigen wir Deine Mithilfe - bitte melde dich per Issue, wenn du Anwender in einem verschlüsselten POCSAG-Netz und im **legalen** Besitz des dazugehörigen Schlüssels bist.
In der Zukunft wollen wir die Möglichkeit schaffen, codierte Nachrichten zu entschlüsseln (und nur dann, wenn der Schlüssel bekannt ist!), dafür brauchen wir Dich als Tester!
**Readme ist veraltet** - bitte im [Wiki](https://github.com/Schrolli91/BOSWatch/wiki) nachschauen!
**Please** only use Code from **master**-Branch - thats **the only stable!** **Please** only use Code from **master**-Branch - thats **the only stable!**
beta-branch is for beta-test of new features
unless you are developer you can use the develop-Branch - may be unstable! unless you are developer you can use the develop-Branch - may be unstable!
### Features ### Features
##### Implemented features: ##### Implemented features:
**list is not complete!**
- FMS, ZVEI and POCSAG512/1200/2400 decoding and displaying - FMS, ZVEI and POCSAG512/1200/2400 decoding and displaying
- Plugin support for easy functional extension - Plugin support for easy functional extension
- Filtering double alarms with adjustable time and check width - Filtering double alarms with adjustable time and check width
@ -51,14 +40,31 @@ unless you are developer you can use the develop-Branch - may be unstable!
- Ready for use BOSWatch as daemon - Ready for use BOSWatch as daemon
- possibility to start plugins asynchron - possibility to start plugins asynchron
- NMA Error Handler - NMA Error Handler
- multicastAlarm for transmission optimized networks
##### Features for the future:
- more plugins
- other Ideas per Issues please
### Plugins ###Plugins
If you want to code your own Plugin, see `plugins/README.md`. If you want to code your own Plugin, see `plugins/README.md`.
##### Implemented plugins: ##### Implemented plugins:
please look at the wiki page
|Plugin|Function|FMS|ZVEI|POC|
|-----|---------|:-:|:--:|:-:|
|MySQL|insert data into MySQL database|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|httpRequest|send a request with parameter to an URL|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|eMail|send Mails with own text|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|BosMon|send data to BosMon server|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|firEmergency|send data to firEmergency server|:x:|:white_check_mark:|:white_check_mark:|
|jsonSocket|send data as jsonString to a socket server|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|NMA|send data to Notify my Android|:white_check_mark:|:white_check_mark:|:white_check_mark:|
- for more Information to the plugins see `config.ini`
##### Plugins for the Future:
- Ideas per Issues please
### Configuration ### Configuration
@ -75,7 +81,7 @@ For the other functions see "Usage" below.
For the RegEX filter functions see Section `[Filters]` For the RegEX filter functions see Section `[Filters]`
http://www.regexr.com/ - RegEX test tool an documentation http://www.regexr.com/ - RegEX test tool an documentation
If RegEX is enabled - only allowed data will pass ! No filter for a combination typ/plugin = all data will pass
Syntax: `INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX` (separator `;`) Syntax: `INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX` (separator `;`)
- `TYP` = the data typ (FMS|ZVEI|POC) - `TYP` = the data typ (FMS|ZVEI|POC)
@ -99,15 +105,52 @@ Take a look into the parser.php for the parsing functions~~
### Usage ### Usage
please look at the wiki page `sudo python boswatch.py -f 85.235M -a FMS ZVEI`
Starts boswatch at frequency 85.235 MHz with the demodulation functions FMS and ZVEI.
Parameter -f/--freq and -a/--demod are required!
Help to all usable parameters with `sudo python boswatch.py -h`
```
usage: boswatch.py [-h] -f FREQ [-d DEVICE] [-e ERROR] -a
{FMS,ZVEI,POC512,POC1200,POC2400}
[{FMS,ZVEI,POC512,POC1200,POC2400} ...] [-s SQUELCH] [-v]
optional arguments:
-h, --help show this help message and exit
-f FREQ, --freq FREQ Frequency you want to listen
-d DEVICE, --device DEVICE Device you want to use (Check with rtl_test)
-e ERROR, --error ERROR Frequency-Error of your device in PPM
-a {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...],
--demod {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...]
Demodulation functions
-s SQUELCH, --squelch SQUELCH level of squelch
-u, --usevarlog Use '/var/log/boswatch' for logfiles instead of subdir 'log' in BOSWatch directory
-v, --verbose Shows more information
-q, --quiet Shows no information. Only logfiles
```
### Installation ### Installation
please look at the wiki page Please follow the instructions written down in the wiki:
https://github.com/Schrolli91/BOSWatch/wiki
You just need to download a single file since the installer manages the whole process except the installation of a webserver and a database.
If you want to use BOSWatch as a daemon, you have to set your If you want to use BOSWatch as a daemon, you have to set your
configuration in `service/boswatch.sh` and copy it to `/etc/init.d`. configuration in `service/boswatch.sh` and copy it to `/etc/init.d`.
Then you can start BOSWatch with `sudo /etc/init.d/boswatch.sh start`. Then you can start BOSWatch with `sudo /etc/init.d/boswatch.sh start`.
For configuration-details see `service/README.md`. For configuration-details see `service/README.md`.
##### Big thanks ### Requirements
to smith_fms and McBo from Funkmeldesystem.de - Forum for Inspiration and Groundwork! - RTL_SDR (rtl_fm)
- Multimon-NG
- Python Support
- MySQL Connector for Python (for MySQL-plugin)
Thanks to smith_fms and McBo from Funkmeldesystem.de - Forum for Inspiration and Groundwork!
### Code your own Plugin
See `plugins/README.md`

View file

@ -21,7 +21,6 @@ import logging.handlers
import argparse # for parse the args import argparse # for parse the args
import ConfigParser # for parse the config file import ConfigParser # for parse the config file
import os # for log mkdir import os # for log mkdir
import sys # for py version
import time # for time.sleep() import time # for time.sleep()
import subprocess # for starting rtl_fm and multimon-ng import subprocess # for starting rtl_fm and multimon-ng
@ -163,9 +162,7 @@ try:
# #
try: try:
logging.debug("SW Version: %s",globalVars.versionNr) logging.debug("SW Version: %s",globalVars.versionNr)
logging.debug("Branch: %s",globalVars.branch)
logging.debug("Build Date: %s",globalVars.buildDate) logging.debug("Build Date: %s",globalVars.buildDate)
logging.debug("Python Vers: %s",sys.version)
logging.debug("BOSWatch given arguments") logging.debug("BOSWatch given arguments")
if args.test: if args.test:
logging.debug(" - Test-Mode!") logging.debug(" - Test-Mode!")
@ -181,7 +178,7 @@ try:
demodulation += "-a FMSFSK " demodulation += "-a FMSFSK "
logging.debug(" - Demod: FMS") logging.debug(" - Demod: FMS")
if "ZVEI" in args.demod: if "ZVEI" in args.demod:
demodulation += "-a ZVEI1 " demodulation += "-a ZVEI2 "
logging.debug(" - Demod: ZVEI") logging.debug(" - Demod: ZVEI")
if "POC512" in args.demod: if "POC512" in args.demod:
demodulation += "-a POCSAG512 " demodulation += "-a POCSAG512 "
@ -220,14 +217,9 @@ try:
# if given loglevel is debug: # if given loglevel is debug:
if globalVars.config.getint("BOSWatch","loglevel") == 10: if globalVars.config.getint("BOSWatch","loglevel") == 10:
configHandler.checkConfig("BOSWatch") configHandler.checkConfig("BOSWatch")
configHandler.checkConfig("multicastAlarm")
configHandler.checkConfig("Filters")
configHandler.checkConfig("FMS") configHandler.checkConfig("FMS")
configHandler.checkConfig("ZVEI") configHandler.checkConfig("ZVEI")
configHandler.checkConfig("POC") configHandler.checkConfig("POC")
configHandler.checkConfig("Plugins")
configHandler.checkConfig("Filters")
#NMAHandler is outputed below
except: except:
# we couldn't work without config -> exit # we couldn't work without config -> exit
logging.critical("cannot read config file") logging.critical("cannot read config file")
@ -249,6 +241,29 @@ try:
logging.debug("cannot set loglevel of fileHandler", exc_info=True) logging.debug("cannot set loglevel of fileHandler", exc_info=True)
#
# Add NMA logging handler
#
try:
if configHandler.checkConfig("NMAHandler"):
# is NMAHandler enabled?
if globalVars.config.getboolean("NMAHandler", "enableHandler") == True:
# we only could do something, if an APIKey is given:
if len(globalVars.config.get("NMAHandler","APIKey")) > 0:
logging.debug("add NMA logging handler")
from includes import NMAHandler
if globalVars.config.get("NMAHandler","appName") == "":
nmaHandler = NMAHandler.NMAHandler(globalVars.config.get("NMAHandler","APIKey"))
else:
nmaHandler = NMAHandler.NMAHandler(globalVars.config.get("NMAHandler","APIKey"), globalVars.config.get("NMAHandler","appName"))
nmaHandler.setLevel(globalVars.config.getint("NMAHandler","loglevel"))
myLogger.addHandler(nmaHandler)
except:
# It's an error, but we could work without that stuff...
logging.error("cannot add NMA logging handler")
logging.debug("cannot add NMA logging handler", exc_info=True)
# initialization was fine, continue with main program... # initialization was fine, continue with main program...
# #

View file

@ -114,26 +114,6 @@ ricd = Unwetter
# Usually sent periodically, separated by comma # Usually sent periodically, separated by comma
netIdent_ric = 0174760, 1398098 netIdent_ric = 0174760, 1398098
[multicastAlarm]
# Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks).
# In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before.
# A POCSAG pager (DME) can be configured to start an acoustic alarm if a specific RIC without text has been received. If afterwards the specific RIC with the text message will be received, the pager will show the message in it's display.
# multicastAlarm enables BOSwatch to process the all received RICs joined with the text message.
#
# enable multicastAlarm (0 - off | 1 - on)
multicastAlarm = 0
# time limit for alarms that do not belong to the multicastAlarm sequence in seconds
multicastAlarm_ignore_time = 15
# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence) (can be empty)
multicastAlarm_delimiter_ric =
# multicastAlarm RIC that is used to send the text message
multicastAlarm_ric =
[Filters] [Filters]
# RegEX Filter Configuration # RegEX Filter Configuration
# http://www.regexr.com/ - RegEX Test Tool an Documentation # http://www.regexr.com/ - RegEX Test Tool an Documentation
@ -174,10 +154,8 @@ template = 0
[MySQL] [MySQL]
# MySQL configuration # MySQL configuration
#default port: 3306
dbserver = localhost dbserver = localhost
dbport = 3306 dbuser = root
dbuser = boswatch
dbpassword = root dbpassword = root
database = boswatch database = boswatch

View file

@ -114,26 +114,6 @@ ricd = Unwetter
# Usually sent periodically, separated by comma # Usually sent periodically, separated by comma
netIdent_ric = 0174760, 1398098 netIdent_ric = 0174760, 1398098
[multicastAlarm]
# Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks).
# In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before.
# A POCSAG pager (DME) can be configured to start an acoustic alarm if a specific RIC without text has been received. If afterwards the specific RIC with the text message will be received, the pager will show the message in it's display.
# multicastAlarm enables BOSwatch to process the all received RICs joined with the text message.
#
# enable multicastAlarm (0 - off | 1 - on)
multicastAlarm = 0
# time limit for alarms that do not belong to the multicastAlarm sequence in seconds
multicastAlarm_ignore_time = 15
# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence) (can be empty)
multicastAlarm_delimiter_ric =
# multicastAlarm RIC that is used to send the text message
multicastAlarm_ric =
[Filters] [Filters]
# RegEX Filter Configuration # RegEX Filter Configuration
# http://www.regexr.com/ - RegEX Test Tool an Documentation # http://www.regexr.com/ - RegEX Test Tool an Documentation
@ -174,9 +154,7 @@ template = 0
[MySQL] [MySQL]
# MySQL configuration # MySQL configuration
#default port: 3306
dbserver = localhost dbserver = localhost
dbport = 3306
dbuser = boswatch dbuser = boswatch
dbpassword = root dbpassword = root
database = boswatch database = boswatch

View file

@ -35,7 +35,7 @@ POCSAG512: Address: 1000003 Function: 3 Alpha: BOSWatch-Test: okay
POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay
POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test öäü: okay POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test öäü: okay
# with csv # witch csv
POCSAG512: Address: 1234567 Function: 1 Alpha: BOSWatch-Test: with csv POCSAG512: Address: 1234567 Function: 1 Alpha: BOSWatch-Test: with csv
# without csv # without csv
@ -86,22 +86,6 @@ POCSAG1200: Address: 7777777 Function: 1 Alpha: BOSWatch-Test: denied
POCSAG1200: Address: 0000004 Function: 1 Alpha: BOSWatch-Test: out of filter start POCSAG1200: Address: 0000004 Function: 1 Alpha: BOSWatch-Test: out of filter start
POCSAG1200: Address: 9000000 Function: 1 Alpha: BOSWatch-Test: out of filter end POCSAG1200: Address: 9000000 Function: 1 Alpha: BOSWatch-Test: out of filter end
#Probealram
POCSAG1200: Address: 0871004 Function: 1 Alpha: Dies ist ein Probealarm!
## Multicast Alarm
POCSAG1200: Address: 0871002 Function: 0 Alpha: <EOT><FF>
POCSAG1200: Address: 0860001 Function: 0
POCSAG1200: Address: 0860002 Function: 0
POCSAG1200: Address: 0860003 Function: 0
POCSAG1200: Address: 0860004 Function: 0
POCSAG1200: Address: 0860005 Function: 0
POCSAG1200: Address: 0860006 Function: 0
POCSAG1200: Address: 0860007 Function: 0
POCSAG1200: Address: 0860008 Function: 0
POCSAG1200: Address: 0860009 Function: 0
POCSAG1200: Address: 0860010 Function: 0
POCSAG1200: Address: 0871003 Function: 0 Alpha: B2 Feuer Gebäude Pers in Gefahr. bla bla bla<NUL>
# regEx-Filter? # regEx-Filter?
@ -122,15 +106,15 @@ FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Stat
# #
#with csv description #with csv description
ZVEI1: 12345 ZVEI2: 12345
#without csv description #without csv description
ZVEI1: 56789 ZVEI2: 56789
#duplicate #duplicate
ZVEI1: 56789 ZVEI2: 56789
#with repeat Tone #with repeat Tone
ZVEI1: 1F2F3 ZVEI2: 1F2F3
#in case of invalid id #in case of invalid id
ZVEI1: 135 ZVEI2: 135
#in case of a double-tone for siren n-'D's are sended #in case of a double-tone for siren n-'D's are sended
ZVEI1: DDD ZVEI2: DDD
ZVEI1: DDDDD ZVEI2: DDDDD

View file

@ -1,17 +1,6 @@
########################
# ____ ____ ______ __ __ __ # BOSWatch Config File #
# / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ ########################
# / __ / / / /\__ \| | /| / / __ / __/ ___/ __ \
# / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / /
# /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/
# German BOS Information Script
# configuration file
#
# The intercept of the German BOS radio is strictly
# prohibited and will be prosecuted.
# The use is only authorized personnel permitted!
#
[BOSWatch] [BOSWatch]
# set loglevel for logfile # set loglevel for logfile
@ -42,7 +31,6 @@ processAlarmAsync = 0
# Using RegEx-filter (0 - off | 1 - on) # Using RegEx-filter (0 - off | 1 - on)
# filter-configuration in section [Filters] # filter-configuration in section [Filters]
# if you are using the RegEx filter you must add filter rules to forward alarms
useRegExFilter = 0 useRegExFilter = 0
# for double check save the last n IDs # for double check save the last n IDs
@ -61,7 +49,22 @@ doubleFilter_ignore_time = 5
doubleFilter_check_msg = 0 doubleFilter_check_msg = 0
# writes the multimon-ng raw data stream into a text file named mm_raw.txt # writes the multimon-ng raw data stream into a text file named mm_raw.txt
writeMultimonRaw = 0 writeMultimonRaw = 1
[NMAHandler]
# you can use a logging handler for sending logging records to NotifyMyAndroid
# enableHandler (0|1) will enable the NMA handler
enableHandler = 0
# loglevel for NMAHandler (see BOSWatch loglevel description)
loglevel = 50
# logging record will send to APIKey
APIKey =
# you can change the name of the application (default: BOSWatch)
# (f.e. if you use more than one instance of BOSWatch)
appName = BOSWatch
[FMS] [FMS]
@ -76,14 +79,12 @@ idDescribed = 0
# Better use RegEX to verify the correct data # Better use RegEX to verify the correct data
checkCRC = 0 checkCRC = 0
[ZVEI] [ZVEI]
# look-up-table for adding a description # look-up-table for adding a description
# using description (0 - off | 1 - on) # using description (0 - off | 1 - on)
# descriptions are loaded from csv/zvei.csv # descriptions are loaded from csv/zvei.csv
idDescribed = 0 idDescribed = 0
[POC] [POC]
# some very simple filters: # some very simple filters:
# Allow only this RICs (empty: allow all, separator ",") # Allow only this RICs (empty: allow all, separator ",")
@ -103,11 +104,6 @@ filter_range_end = 9999999
# descriptions are loaded from csv/poc.csv # descriptions are loaded from csv/poc.csv
idDescribed = 0 idDescribed = 0
# change between Main-RIC with Sub-RIC (0 - off)
# or only the Sub-RIC (1 - on)
# descriptions are loaded from csv/poc.csv
onlysubric = 0
# Static Massages for Subrics. # Static Massages for Subrics.
rica = Feuer rica = Feuer
ricb = TH ricb = TH
@ -117,40 +113,11 @@ ricd = Unwetter
# RIC for net identification # RIC for net identification
# Usually sent periodically, separated by comma # Usually sent periodically, separated by comma
netIdent_ric = 0174760, 1398098 netIdent_ric = 0174760, 1398098
# you can hold one entry per netIdent_ric [0] or the whole history [1]
netIdent_history = 0
# With some message, coordinates can be sent to determine the destination
# Do you want to enable this feature? (0 - off | 1 - on)
geo_enable = 0
# If a RIC contains coordinates, specify the regex used to decode them
geo_format = #C(\d{2})(\d{5}),(\d{2})(\d{5})#
geo_order = LON, lon, LAT, lat
[multicastAlarm]
# Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks).
# In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before.
# A POCSAG pager (DME) can be configured to start an acoustic alarm if a specific RIC without text has been received. If afterwards the specific RIC with the text message will be received, the pager will show the message in it's display.
# multicastAlarm enables BOSwatch to process the all received RICs joined with the text message.
#
# enable multicastAlarm (0 - off | 1 - on)
multicastAlarm = 0
# time limit for alarms that do not belong to the multicastAlarm sequence in seconds
multicastAlarm_ignore_time = 15
# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence). Needs to be empty if multicastAlarms are interrupted by normal alarms.
multicastAlarm_delimiter_ric =
# multicastAlarm RIC (one or more, separated by comma) used to send the text message
multicastAlarm_ric =
[Filters] [Filters]
# RegEX Filter Configuration # RegEX Filter Configuration
# http://www.regexr.com/ - RegEX Test Tool an Documentation # http://www.regexr.com/ - RegEX Test Tool an Documentation
# If RegEX is enabled - only alloewd data will pass! # No Filter for a Typ/Plugin Combination = all Data pass
# INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX # INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX
# TYP = the Data Typ (FMS|ZVEI|POC) # TYP = the Data Typ (FMS|ZVEI|POC)
# DATAFIELD = the field of the Data Array (see readme.md in plugin folder) # DATAFIELD = the field of the Data Array (see readme.md in plugin folder)
@ -173,27 +140,21 @@ eMail = 0
BosMon = 0 BosMon = 0
firEmergency = 0 firEmergency = 0
jsonSocket = 0 jsonSocket = 0
notifyMyAndroid = 0
SMS = 0 SMS = 0
Sms77 = 0 Sms77 = 0
FFAgent = 0 FFAgent = 0
Pushover = 0 Pushover = 0
Telegram = 0 Telegram = 0
yowsup = 0 yowsup = 0
hue = 0
Divera = 0
gpiocontrol = 0
fhemCmd = 0
mqtt = 0
# for developing - template-module # for developing template-module
template = 0 template = 0
[MySQL] [MySQL]
# MySQL configuration # MySQL configuration
#default port: 3306
dbserver = localhost dbserver = localhost
dbport = 3306
dbuser = boswatch dbuser = boswatch
dbpassword = root dbpassword = root
database = boswatch database = boswatch
@ -208,20 +169,36 @@ tableSIG = bos_signal
[httpRequest] [httpRequest]
# example URL http://example.com/remote.php?DESCR=%DESCR% # example URL http://example.com/remote.php?DESCR=%DESCR%
# multiple URLs can be separated by comma # you can use the following wildcards in your URL as GET params:
# you can use the wildcards in your URL as GET params:
# http://en.wikipedia.org/wiki/Query_string # http://en.wikipedia.org/wiki/Query_string
#Wildcards can be used, see end of the file! # %FMS% = FMS Code
# %STATUS% = FMS Status
# %DIR% = Direction of the telegram (0/1)
# %DIRT% = Direction of the telegram (Text-String)
# %TSI% = Tactical Short Information (I-IV)
# %DESCR% = Description from csv-file
# %TIME% = Time (by script)
# %DATE% = Date (by script)
#fms_url = http://www.google.de?code=%FMS%&stat=%STATUS% #fms_url = http://www.google.de?code=%FMS%&stat=%STATUS%
fms_url = fms_url =
#Wildcards can be used, see end of the file! # %ZVEI% = ZVEI 5-tone Code
# %DESCR% = Description from csv-file
# %TIME% = Time (by script)
# %DATE% = Date (by script)
#zvei_url = http://www.google.de?zvei=%ZVEI% #zvei_url = http://www.google.de?zvei=%ZVEI%
zvei_url = zvei_url =
#Wildcards can be used, see end of the file! # %RIC% = POCSAG RIC
# %FUNC% = POCSAG function/Subric (1-4)
# %FUNCCHAR% = POCSAG function/Subric as character (a-d)
# %FUNCTEXT% = POCSAG function/Subric static massage definded in POCSAG section
# %MSG% = Message of the POCSAG telegram
# %BITRATE% = Bitrate of the POCSAG telegram
# %DESCR% = Description from csv-file
# %TIME% = Time (by script)
# %DATE% = Date (by script)
#poc_url = http://www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG% #poc_url = http://www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG%
poc_url = poc_url =
@ -246,15 +223,42 @@ to = user@irgendwo, user2@woanders
# normal|urgent|non-urgent # normal|urgent|non-urgent
priority = urgent priority = urgent
#Wildcards can be used, see end of the file! # %FMS% = FMS Code
# %STATUS% = FMS Status
# %DIR% = Direction of the telegram (0/1)
# %DIRT% = Direction of the telegram (Text-String)
# %TSI% = Tactical Short Information (I-IV)
# %DESCR% = Description, if description-module is used
# %DATE% = Date (by script)
# %TIME% = Time (by script)
# %BR% = Insert line wrap (only in message)
# %LPAR% = (
# %RPAR% = )
fms_subject = FMS: %FMS% fms_subject = FMS: %FMS%
fms_message = %DATE% %TIME%: %FMS%%BR%Status: %STATUS% - Direction: %DIRT% - TSI: %TSI% fms_message = %DATE% %TIME%: %FMS%%BR%Status: %STATUS% - Direction: %DIRT% - TSI: %TSI%
#Wildcards can be used, see end of the file! # %ZVEI% = ZVEI 5-tone Code
# %DESCR% = Description, if description-module is used
# %DATE% = Date (by script)
# %TIME% = Time (by script)
# %BR% = Insert line wrap (only in message)
# %LPAR% = (
# %RPAR% = )
zvei_subject = Alarm: %ZVEI% zvei_subject = Alarm: %ZVEI%
zvei_message = %DATE% %TIME%: %ZVEI% zvei_message = %DATE% %TIME%: %ZVEI%
#Wildcards can be used, see end of the file! # %RIC% = POCSAG RIC
# %FUNC% = POCSAG function/Subric (1-4)
# %FUNCCHAR% = POCSAG function/Subric als character (a-d)
# %FUNCTEXT% = POCSAG function/Subric static massage definded in POCSAG section
# %MSG% = Message of the POCSAG telegram
# %BITRATE% = Bitrate of the POCSAG telegram
# %DESCR% = Description, if description-module is used
# %DATE% = Date (by script)
# %TIME% = Time (by script)
# %BR% = Insert line wrap (only in message)
# %LPAR% = (
# %RPAR% = )
poc_subject = Alarm: %RIC%%LPAR%%FUNCCHAR%%RPAR% poc_subject = Alarm: %RIC%%LPAR%%FUNCCHAR%%RPAR%
poc_message = %DATE% %TIME% - %DESCR%: %MSG% poc_message = %DATE% %TIME% - %DESCR%: %MSG%
@ -287,6 +291,23 @@ server = 192.168.0.1
port = 8888 port = 8888
[notifyMyAndroid]
# APIKey given from notifyMyAndroid
APIKey =
# Priority goes from -2 (lowest) to 2 (highest). The default priority is 0 (normal)
priority = 0
# You can change the name of the application (default: BOSWatch)
# (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 priority above will be ignored, if you use a csv
# configuration loaded from csv/nma.csv
usecsv = 0
[SMS] [SMS]
# be aware that you need 'gammu' installed and running # be aware that you need 'gammu' installed and running
# at least you need an UMTS-stick which is supported by 'gammu' # at least you need an UMTS-stick which is supported by 'gammu'
@ -360,46 +381,14 @@ api_key =
# Pushover Userkey or Groupkey to receive message # Pushover Userkey or Groupkey to receive message
user_key = user_key =
# Pushover Sound # Title of the message
sound = title = BOSWatch Message
# Section for POCSAG
# Adapt Pocsag Subric (a,b,c,d) to Pushover Priorities (see https://pushover.net/api#priority) # Adapt Pocsag Subric (a,b,c,d) to Pushover Priorities (see https://pushover.net/api#priority)
SubA = 1 SubA = 0
SubB = 1 SubB = 2
SubC = 2 SubC = 1
SubD = -2 SubD = 0
# ...or define the priority depending on the RIC
# 1. switch it on
poc_spec_ric = 0
# 2. fill the following lists, seperator is ,
poc_prio2 =
poc_prio1 =
poc_prio0 =
poc_title = Alarm: %RIC%%LPAR%%FUNCCHAR%%RPAR%
poc_message = %DATE% %TIME% - %DESCR%: %MSG%
# Section for ZVEI
# default prio for all ZVEI - except you specify it different
zvei_std_prio = 1
# [1 - on, 0 - off] - by switching on you can list certain ZVEI depending on their priority
zvei_sep_prio = 0
# use the following lists, separator is ,
zvei_prio2 =
zvei_prio1 =
zvei_prio0 =
zvei_title = Alarm: %ZVEI%
zvei_message = %DATE% %TIME%: %ZVEI%
# Section for FMS
fms_prio = 1
fms_title = FMS: %FMS%
fms_message = %DATE% %TIME%: %FMS%%BR%Status: %STATUS% - Direction: %DIRT% - TSI: %TSI% %LPAR%%DESCR%%RPAR%
# how often should Pushover re-alert in seconds (emergency-messages) # how often should Pushover re-alert in seconds (emergency-messages)
retry = 30 retry = 30
@ -423,22 +412,6 @@ RICforLocationAPIKey =
# This is your Google API key. # This is your Google API key.
# Required if you want to create a map based on location information received with the above RIC. # Required if you want to create a map based on location information received with the above RIC.
GoogleAPIKey = GoogleAPIKey =
# Define your start address for the routing
# Use the following format: CityOfOrigin+Street+Number
RoutingOrigin = MyCity+MyStreet+MyNumber
# Wildcards can be used, see end of the file!
# In addition, feel free to use simple HTML stlye tags like <b>%FMS%</b>
FMS_message = %DATE% %TIME%: %FMS%
#W ildcards can be used, see end of the file!
# In addition, feel free to use simple HTML stlye tags like <b>%ZVEI%</b>
ZVEI_message = %DATE% %TIME%: %ZVEI%
# Wildcards can be used, see end of the file!
# In addition, feel free to use simple HTML stlye tags like <b>%DESCR%</b>
POC_message = %DESCR%%BR%%MSG%
[yowsup] [yowsup]
# number or chat-number who whants to become the news # number or chat-number who whants to become the news
@ -448,128 +421,6 @@ sender =
# password from this number # password from this number
password= password=
#Wildcards can be used, see end of the file!
fms_message = %DATE% %TIME%: %FMS%
#Wildcards can be used, see end of the file!
zvei_message = %DATE% %TIME%: %ZVEI%
#Wildcards can be used, see end of the file!
poc_message = %MSG%
[hue]
# For API access please read https://www.developers.meethue.com/documentation/getting-started (registration required)
# or https://www.google.com/search?q=philips+hue+how+to+get+api+key
# IP address of the hue bridge
bridgeip =
# the numeric ID of the device (only one device supported)
deviceid =
# the authentication string used to access the bridge
apikey =
# Timing parameters for switching on/off
# Every on/off cycle adds delay to BOSwatch if it operates in synchronous mode. Consider to configure "processAlarmAsync = 1" to activate asynchronous operation in BOSwatch if you switch on/off multiple times.
# If a light bulb is connected, you can keep it blinking for a while
repeat = 2
timeon = 2
timeoff = 1
# configure 0 to keep the switch on for infinite time or configure >=1 to keep it for the value in seconds to on, before switching to off.
keepon = 60
[Divera]
# See https://api.divera247.com/ for Api-Documentation
# Title: Alarm-Stichwort (max. 50 Zeichen, in der kostenlosen Version max. 30 Zeichen)
# Text: Alarm-Text (in der kostenlosen Version wird dies nicht übernommen)
# Priority: true / false.
# Wird ein anderer Wert oder gar keiner gesetzt, so wird die Divera-Alarmierung nicht durchgeführt.
# Divera API Key
accesskey =
# Section for POCSAG
# Adapt Pocsag Subric (a,b,c,d) to Divera Priorities (true/false)
SubA = true
SubB = false
SubC =
SubD =
poc_title = %DESCR%: %MSG%
poc_text = %DATE% %TIME% - %DESCR%: %MSG%
# poc_ric is alarm-RIC in divera
poc_ric = %DESCR%
# Section for ZVEI
# default prio for all ZVEI - except you specify it different
zvei_prio = true
zvei_title = Alarm: %ZVEI%
zvei_text = %DATE% %TIME%: %ZVEI%
# zvei_id working at 6 characters or more; later name of alarm-RIC in divera
zvei_id = %DESCR%
# Section for FMS
fms_prio = true
fms_title = FMS: %FMS%
fms_text = %DATE% %TIME%: %FMS%%BR%Status: %STATUS% - Direction: %DIRT% - TSI: %TSI% %LPAR%%DESCR%%RPAR%
# fms_vehicle is alarm-RIC in divera
fms_vehicle = %DESCR%
[gpiocontrol]
#Pin that will be triggered
#Only tested on Raspberry Pi 3
pin = 21
#Time the Pin will be triggered (in Seconds)
triggertime = 180
#ONLY POC
#POC Rics that trigger PIN (empty: allow all, separator ",")
activerics = 1234567,1234568
[fhemCmd]
# choose one of "http", "https" or "telnet"
protocol = http
# servername or IP address
server = 192.168.0.1
# numeric port
port = 8083
# username if required
username = dummyUser
# password if required
password = dummyPassword
# desired command to execute
commandFMS = set SteckdoseSchlafzimmerEinsatz on-for-timer 90
commandZVEI =
commandPOC =
[mqtt]
#Adress from MQTT-Broker
brokeraddress = 192.168.178.27
topic = alarm/data
#####################
##### Not ready yet #
#####################
[template]
test1 = testString
test2 = 123456
#####################
##### WILDCARDS #####
#####################
##### FMS
# %FMS% = FMS Code # %FMS% = FMS Code
# %STATUS% = FMS Status # %STATUS% = FMS Status
# %DIR% = Direction of the telegram (0/1) # %DIR% = Direction of the telegram (0/1)
@ -578,29 +429,35 @@ test2 = 123456
# %DESCR% = Description, if description-module is used # %DESCR% = Description, if description-module is used
# %DATE% = Date (by script) # %DATE% = Date (by script)
# %TIME% = Time (by script) # %TIME% = Time (by script)
# %BR% = Insert line wrap (only in message)
# %LPAR% = ( # %LPAR% = (
# %RPAR% = ) # %RPAR% = )
fms_message = %DATE% %TIME%: %FMS%
##### ZVEI
# %ZVEI% = ZVEI 5-tone Code # %ZVEI% = ZVEI 5-tone Code
# %DESCR% = Description, if description-module is used # %DESCR% = Description, if description-module is used
# %DATE% = Date (by script) # %DATE% = Date (by script)
# %TIME% = Time (by script) # %TIME% = Time (by script)
# %BR% = Insert line wrap (only in message)
# %LPAR% = ( # %LPAR% = (
# %RPAR% = ) # %RPAR% = )
zvei_message = %DATE% %TIME%: %ZVEI%
##### POCSAG
# %RIC% = POCSAG RIC # %RIC% = POCSAG RIC
# %FUNC% = POCSAG function/Subric (1-4) # %FUNC% = POCSAG function/Subric (1-4)
# %FUNCCHAR% = POCSAG function/Subric als character (a-d) # %FUNCCHAR% = POCSAG function/Subric als character (a-d)
# %FUNCTEXT% = POCSAG static Subric message (see [POC])
# %MSG% = Message of the POCSAG telegram # %MSG% = Message of the POCSAG telegram
# %BITRATE% = Bitrate of the POCSAG telegram # %BITRATE% = Bitrate of the POCSAG telegram
# %DESCR% = Description, if description-module is used # %DESCR% = Description, if description-module is used
# %DATE% = Date (by script) # %DATE% = Date (by script)
# %TIME% = Time (by script) # %TIME% = Time (by script)
# %BR% = Insert line wrap (only in message)
# %LPAR% = ( # %LPAR% = (
# %RPAR% = ) # %RPAR% = )
poc_message = %MSG%
#####################
##### Not ready yet #
#####################
[template]
test1 = testString
test2 = 123456

View file

Can't render this file because it has a wrong number of fields in line 2.

View file

Can't render this file because it has a wrong number of fields in line 2.

10
csv/poc.csv Normal file
View file

@ -0,0 +1,10 @@
ric,description
#
# BOSWatch CSV file for describing POCSAG-Addresses
#
# For each RIC-Address you could set a description-text
# Use the structure: ric,"Description-Text"
#
# !!! DO NOT delete the first line !!!
#
1234567,"POCSAG testdata äöüß"
Can't render this file because it has a wrong number of fields in line 2.

View file

@ -1,22 +0,0 @@
ric,description
#
# BOSWatch CSV file for describing POCSAG-Addresses
#
# For each RIC-Address you could set a description-text
# Use the structure: ric,"Description-Text"
#
# main RIC with subric:
# You can even define specific subrics, therefore you
# 1. need to specify a main RIC: 1234567, "Unit One"
# 2. specify a certain subric: 1234567B, "Subunit Bravo"
# The result for 1234567B will be "Unit One Subunit Bravo"
# - Be sure having defined the main RIC (step one)! -
#
# Only subric:
# Specify only the subric: 123457B, "Subunit Bravo"
# The result for 1234567B will be "Subunit Bravo"
# - main RIC is not required -
#
# !!! DO NOT delete the first line !!!
#
1234567,"POCSAG testdata äöüß"
Can't render this file because it has a wrong number of fields in line 2.

View file

Can't render this file because it has a wrong number of fields in line 2.

View file

@ -36,7 +36,7 @@ CREATE TABLE `login` (
LOCK TABLES `login` WRITE; LOCK TABLES `login` WRITE;
/*!40000 ALTER TABLE `login` DISABLE KEYS */; /*!40000 ALTER TABLE `login` DISABLE KEYS */;
INSERT INTO `login` VALUES (1,'admin',md5('admin')); INSERT INTO `login` VALUES ,(1,'Test','098f6bcd4621d373cade4e832627b4f6');
/*!40000 ALTER TABLE `login` ENABLE KEYS */; /*!40000 ALTER TABLE `login` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

View file

@ -1,8 +1,8 @@
<?php <?php
$dbhost = "localhost"; $dbhost = "localhost";
$dbuser = "boswatch"; $dbuser = "USERNAME";
$dbpassword = "boswatch"; $dbpassword = "PASSWORD";
$database = "boswatch"; $database = "boswatch";
$tableFMS = "bos_fms"; $tableFMS = "bos_fms";

View file

@ -1,57 +0,0 @@
<?php
session_start();
?>
<?php
if(($_SESSION["username"])!="admin")
{
echo "Sie sind nicht berechtigt fuer diesen Bereich";
exit;
}
?>
<?php
@require_once("config.php");
$verbindung = mysqli_connect($dbhost, $dbuser , $dbpassword, $database)
or die("Verbindung zur Datenbank konnte nicht hergestellt werden");
$username = $_POST["username"];
$passwort = $_POST["passwort"];
$passwort2 = $_POST["passwort2"];
if($passwort != $passwort2 OR $username == "" OR $passwort == "")
{
echo "Eingabefehler. Bitte alle Felder korekt ausfüllen. <a href=\"eintragen.html\">Zurück</a>";
exit;
}
$passwort = md5($passwort);
$result = mysqli_query($verbindung, "SELECT id FROM login WHERE username LIKE '$username'");
$menge = mysqli_num_rows($result);
if($menge == 0)
{
$eintrag = "INSERT INTO login (username, passwort) VALUES ('$username', '$passwort')";
$eintragen = mysqli_query($verbindung, $eintrag);
if($eintragen == true)
{
echo "Benutzername <b>$username</b> wurde erstellt. <a href=\"index.php\">Login</a>";
}
else
{
echo "Fehler beim Speichern des Benutzernames. <a href=\"eintragen.html\">Zurück</a>";
}
}
else
{
echo "Benutzername schon vorhanden. <a href=\"eintragen.html\">Zurück</a>";
}
?>

View file

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<!--
Escape Velocity by HTML5 UP
html5up.net | @n33co
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<?php
session_start();
?>
<?php
if(!isset($_SESSION["username"]))
{
echo "Bitte erst <a href=\"login.html\">einloggen</a>";
exit;
}
?>
<?php
require_once ("config.php");
require_once ("tpl/parser.php");
require_once ("tpl/mysql.class.php");
$db = new Database($dbhost, $dbuser, $dbpassword, $database, 1); //Show Error = 1!
?>
<html>
<head>
<title>BOSwatch</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<!--[if lte IE 8]><script src="css/ie/html5shiv.js"></script><![endif]-->
<script src="js/jquery.min.js"></script>
<script src="js/jquery.dropotron.min.js"></script>
<script src="js/skel.min.js"></script>
<script src="js/skel-layers.min.js"></script>
<script src="js/init.js"></script>
<noscript>
<link rel="stylesheet" href="css/skel.css" />
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/style-desktop.css" />
</noscript>
<!--[if lte IE 8]><link rel="stylesheet" href="css/ie/v8.css" /><![endif]-->
</head>
<body class="no-sidebar">
<!-- Header -->
<div id="header-wrapper" class="wrapper">
<div id="header">
<!-- Nav -->
<nav id="nav">
<ul>
<li><a href="show_pocsag.php">POCSAG</a></li>
<li><a href="show_fms.php">FMS</a></li>
<li><a href="show_zvei.php">ZVEI</a></li>
<li><a href="prefs.php">Einstellungen</a></li>
<li><a href="eintragen.html">Nutzer anlegen</a></li>
</ul>
</nav>
</div>
</div>
<!-- Highlights -->
<div class="wrapper style1">
<div class="title">Usermanagment</div>
<div class="container">
<div style="width: 400px;margin-left:auto;margin-right:auto;text-align:center;">
<form action="eintragen.php" method="post">
Username:<br>
<input type="text" size="12"
name="username"><br>
Passwort:<br>
<input type="password" size="24" maxlength="50"
name="passwort"><br>
Passwort wiederholen:<br>
<input type="password" size="24" maxlength="50"
name="passwort2"><br>
<input type="submit" value="Abschicken">
</form>
</div>
</div>
</div>
</div>
</div>
<div id="copyright">
<ul>
<li style="color:grey;">&copy; BOSWatch</li><li style="color:grey;">Design: <a href="http://html5up.net">HTML5 UP</a></li>
</ul>
</div>
</div>
</body>
</html>

View file

@ -1,111 +1,58 @@
<!DOCTYPE HTML>
<!--
Escape Velocity by HTML5 UP
html5up.net | @n33co
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<?php <?php
session_start(); session_start();
?> ?>
<?php <?php
if(!isset($_SESSION["username"])) if(($_SESSION["username"])!="Kevin")
{ {
echo "Bitte erst <a href='login.html'>einloggen</a>"; echo "Sie sind nicht berechtigt fuer diesen Bereich";
exit; exit;
} }
?> ?>
<?php <?php
require_once ("config.php"); $verbindung = mysql_connect("localhost", "root" , "kevin21")
require_once ("tpl/parser.php"); or die("Verbindung zur Datenbank konnte nicht hergestellt werden");
require_once ("tpl/mysql.class.php"); mysql_select_db("login") or die ("Datenbank konnte nicht ausgewählt werden");
$db = new Database($dbhost, $dbuser, $dbpassword, $database, 1); //Show Error = 1!
$username = $_POST["username"];
$passwort = $_POST["passwort"];
$passwort2 = $_POST["passwort2"];
if($passwort != $passwort2 OR $username == "" OR $passwort == "")
{
echo "Eingabefehler. Bitte alle Felder korekt ausfüllen. <a href=\"eintragen.html\">Zurück</a>";
exit;
}
$passwort = md5($passwort);
$result = mysql_query("SELECT id FROM login WHERE username LIKE '$username'");
$menge = mysql_num_rows($result);
if($menge == 0)
{
$eintrag = "INSERT INTO login (username, passwort) VALUES ('$username', '$passwort')";
$eintragen = mysql_query($eintrag);
if($eintragen == true)
{
echo "Benutzername <b>$username</b> wurde erstellt. <a href=\"login.html\">Login</a>";
}
else
{
echo "Fehler beim Speichern des Benutzernames. <a href=\"eintragen.html\">Zurück</a>";
}
}
else
{
echo "Benutzername schon vorhanden. <a href=\"eintragen.html\">Zurück</a>";
}
?> ?>
<html>
<head>
<title>BOSwatch</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<!--[if lte IE 8]><script src="css/ie/html5shiv.js"></script><![endif]-->
<script src="js/jquery.min.js"></script>
<script src="js/jquery.dropotron.min.js"></script>
<script src="js/skel.min.js"></script>
<script src="js/skel-layers.min.js"></script>
<script src="js/init.js"></script>
<noscript>
<link rel="stylesheet" href="css/skel.css" />
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/style-desktop.css" />
</noscript>
<!--[if lte IE 8]><link rel="stylesheet" href="css/ie/v8.css" /><![endif]-->
</head>
<body class="no-sidebar">
<!-- Header -->
<div id="header-wrapper" class="wrapper">
<div id="header">
<!-- Nav -->
<nav id="nav">
<ul>
<li><a href="show_pocsag.php">POCSAG</a></li>
<li><a href="show_fms.php">FMS</a></li>
<li><a href="show_zvei.php">ZVEI</a></li>
<li><a href="prefs.php">Einstellungen</a></li>
<li><a href="eintragen.php">Nutzer anlegen</a></li>
</ul>
</nav>
</div>
</div>
<!-- Highlights -->
<div class="wrapper style1">
<div class="title">Usermanagment</div>
<div class="container">
<div style="width: 400px;margin-left:auto;margin-right:auto;text-align:center;">
<form action="do_eintragen.php" method="post">
Username:<br>
<input type="text" size="12"
name="username"><br>
Passwort:<br>
<input type="password" size="24" maxlength="50"
name="passwort"><br>
Passwort wiederholen:<br>
<input type="password" size="24" maxlength="50"
name="passwort2"><br>
<input type="submit" value="Abschicken">
</form>
</div>
</div>
</div>
</div>
</div>
<div id="copyright">
<ul>
<li style="color:grey;">&copy; BOSWatch</li><li style="color:grey;">Design: <a href="http://html5up.net">HTML5 UP</a></li>
</ul>
</div>
</div>
</body>
</html>

View file

@ -40,7 +40,7 @@ $db = new Database($dbhost, $dbuser, $dbpassword, $database, 1); //Show Error =
<!-- Logo --> <!-- Logo -->
<div id="logo"> <div id="logo">
<h1><a href="index.php">Willkommen bei BOSWatch</a></h1> <h1><a href="index.html">Willkommen bei BOSWatch</a></h1>
</div> </div>
<!-- Nav --> <!-- Nav -->
@ -75,14 +75,6 @@ Dein Passwort:<br>
<input type="password" size="24" maxlength="50" <input type="password" size="24" maxlength="50"
name="password"><br> name="password"><br>
Startanzeige:<br>
<select name="view">
<option value="pocsag">POCSAG</option>
<option value="fms">FMS</option>
<option value="zvei">ZVEI</option>
</select><br>
<input type="submit" value="Login"> <input type="submit" value="Login">
</form> </form>

View file

@ -4,44 +4,25 @@ session_start();
<?php <?php
require_once ("config.php"); require_once ("config.php");
$verbindung = mysqli_connect($dbhost, $dbuser , $dbpassword) $verbindung = mysql_connect($dbhost, $dbuser , $dbpassword)
or die("Verbindung zur Datenbank konnte nicht hergestellt werden"); or die("Verbindung zur Datenbank konnte nicht hergestellt werden");
mysqli_select_db($verbindung, $database) or die ("Datenbank konnte nicht ausgewählt werden"); mysql_select_db($tableLOG) or die ("Datenbank konnte nicht ausgewählt werden");
if (!isset($_POST["username"]) XOR !isset($_POST["password"]))
{
echo "Fehlende Eingaben - <a href='index.php'>Login</a>";
exit;
}
$username = $_POST["username"]; $username = $_POST["username"];
$passwort = md5($_POST["password"]); $passwort = md5($_POST["password"]);
$abfrage = "SELECT username, passwort FROM" $tableLOG "WHERE username LIKE '$username' LIMIT 1";
$abfrage = "SELECT username, passwort FROM ".$tableLOG." WHERE username LIKE '$username' LIMIT 1"; $ergebnis = mysql_query($abfrage);
$ergebnis = mysqli_query($verbindung, $abfrage); $row = mysql_fetch_object($ergebnis);
$row = mysqli_fetch_object($ergebnis);
if($row->passwort == $passwort) if($row->passwort == $passwort)
{ {
$_SESSION["username"] = $username; $_SESSION["username"] = $username;
switch ($_POST["view"]) { header("Location: /show_pocsag.php");
case 'pocsag':
header("Location: show_pocsag.php");
break;
case 'zvei':
header("Location: show_zvei.php");
break;
case 'fms':
header("Location: show_fms.php");
break;
default:
header("Location: show_pocsag.php");
}
} }
else else
{ {
echo "Benutzername und/oder Passwort waren falsch. <a href=\"index.php\">Login</a>"; echo "Benutzername und/oder Passwort waren falsch. <a href=\"login.html\">Login</a>";
} }
?> ?>

View file

@ -1,6 +0,0 @@
<?php
session_unset();
header("Location: index.php");
?>

View file

@ -11,7 +11,7 @@ session_start();
?> ?>
<?php <?php
if(($_SESSION["username"])!="admin") if(($_SESSION["username"])!="Test")
{ {
echo "Sie sind nicht berechtigt fuer diesen Bereich"; echo "Sie sind nicht berechtigt fuer diesen Bereich";
exit; exit;
@ -27,7 +27,7 @@ include("tpl/a_header.php");
<div class="wrapper style3"> <div class="wrapper style3">
<div class="title">Einstellungen</div> <div class="title">Einstellungen</div>
<div id="highlights" class="container" style=""> <div id="highlights" class="container" style="">
<!--
<tr> <tr>
<td>Filter Range Start:</td><td> <td>Filter Range Start:</td><td>
<input type="text" size="24" maxlength="50" value=" <input type="text" size="24" maxlength="50" value="
@ -43,7 +43,7 @@ $ini_array = parse_ini_file("config.ini");
echo($ini_array['filter_range_end']); echo($ini_array['filter_range_end']);
?> ?>
"></td> "></td>
</tr>--> </tr>
</div> </div>
</div> </div>

View file

@ -41,8 +41,7 @@
<li><a href="show_fms.php">FMS</a></li> <li><a href="show_fms.php">FMS</a></li>
<li><a href="show_zvei.php">ZVEI</a></li> <li><a href="show_zvei.php">ZVEI</a></li>
<li><a href="prefs.php">Einstellungen</a></li> <li><a href="prefs.php">Einstellungen</a></li>
<li><a href="eintragen.php">Nutzer anlegen</a></li> <li><a href="eintragen.html">Nutzer anlegen</a></li>
<li><a href="logout.php">Ausloggen</a></li>
</ul> </ul>
</nav> </nav>

View file

@ -23,16 +23,16 @@ Simple Database Class (C) by Bastian Schroll
function __construct($host, $user, $password, $database, $show_error = 1) function __construct($host, $user, $password, $database, $show_error = 1)
{ {
$this->show_error = $show_error; $this->show_error = $show_error;
@$this->conn = mysqli_connect($host, $user, $password); @$this->conn = mysql_connect($host, $user, $password);
if ($this->conn == false) if ($this->conn == false)
{ {
$this->error("Keine Verbindung zum Datenbank Server!", mysqli_error($this->conn)); $this->error("Keine Verbindung zum Datenbank Server!", mysql_error());
return false; return false;
} }
if (!@mysqli_select_db($this->conn, $database)) if (!@mysql_select_db($database, $this->conn))
{ {
$this->error("Datenbank nicht gefunden!", mysqli_error($this->conn)); $this->error("Datenbank nicht gefunden!", mysql_error());
return false; return false;
} }
return true; return true;
@ -48,10 +48,10 @@ Simple Database Class (C) by Bastian Schroll
*/ */
function query($query) function query($query)
{ {
$this->result = @mysqli_query($this->conn, $query); $this->result = @mysql_query($query, $this->conn);
if ($this->result == false) if ($this->result == false)
{ {
$this->error("Fehlerhafte Datenbank Anfrage!", mysqli_error($this->conn)); $this->error("Fehlerhafte Datenbank Anfrage!", mysql_error());
return false; return false;
} }
return $this->result; return $this->result;
@ -69,10 +69,10 @@ Simple Database Class (C) by Bastian Schroll
{ {
if ($result != null) if ($result != null)
{ {
return @mysqli_fetch_assoc($result); return @mysql_fetch_assoc($result);
} else } else
{ {
return @mysqli_fetch_assoc($this->result); return @mysql_fetch_assoc($this->result);
} }
} }

View file

@ -10,7 +10,7 @@
</tr> </tr>
<?php <?php
$db->query("SELECT id, time, ric, function, msg, description FROM ".$tablePOC." ORDER BY id DESC LIMIT 100"); $db->query("SELECT id, time, ric, funktion, text, description FROM ".$tablePOC." ORDER BY id DESC LIMIT 100");
$Rows = array(); $Rows = array();
while ($daten = $db->fetchAssoc()) while ($daten = $db->fetchAssoc())
{ {
@ -29,10 +29,10 @@
// echo "<td>". $poc['id'] . "</td>"; // echo "<td>". $poc['id'] . "</td>";
// //
echo "<td>". $poc['description'] . "</td>"; echo "<td>". $poc['description'] . "</td>";
echo "<td>". $poc['msg'] . "</td>"; echo "<td>". $poc['text'] . "</td>";
echo "<td>". $time . "</td>"; echo "<td>". $time . "</td>";
echo "<td>". $poc['ric'] . "</td>"; echo "<td>". $poc['ric'] . "</td>";
echo "<td>". $poc['function'] . "</td>"; echo "<td>". $poc['funktion'] . "</td>";
echo "</tr>"; echo "</tr>";
} }
?> ?>

View file

@ -23,39 +23,35 @@ Simple Database Class (C) by Bastian Schroll
function __construct($host, $user, $password, $database, $show_error = 1) function __construct($host, $user, $password, $database, $show_error = 1)
{ {
$this->show_error = $show_error; $this->show_error = $show_error;
@$this->conn = mysqli_connect($host, $user, $password); @$this->conn = mysql_connect($host, $user, $password);
if ($this->conn == false) if ($this->conn == false)
{ {
$this->error("Keine Verbindung zum Datenbank Server!", mysqli_error($this->conn)); $this->error("Keine Verbindung zum Datenbank Server!", mysql_error());
return false; return false;
} }
if (!@mysqli_select_db($this->conn, $database)) if (!@mysql_select_db($database, $this->conn))
{ {
$this->error("Datenbank nicht gefunden!", mysqli_error($this->conn)); $this->error("Datenbank nicht gefunden!", mysql_error());
return false; return false;
} }
/* Set character set for database connection to utf8mb4 */
mysqli_query($this->conn, "SET NAMES 'utf8mb4'");
return true; return true;
} }
/** /**
* Database::query() * Database::query()
* *
* Fuehrt einen MySQL Query aus * F<EFBFBD>hrt einen MySQL Query aus
* *
* @param mixed $query Auszufuehrender Query * @param mixed $query Auszuf<EFBFBD>hrender Query
* @return Result-Handler/FALSE * @return Result-Handler/FALSE
*/ */
function query($query) function query($query)
{ {
$this->result = @mysqli_query($this->conn, $query); $this->result = @mysql_query($query, $this->conn);
if ($this->result == false) if ($this->result == false)
{ {
$this->error("Fehlerhafte Datenbank Anfrage!", mysqli_error($this->conn)); $this->error("Fehlerhafte Datenbank Anfrage!", mysql_error());
return false; return false;
} }
return $this->result; return $this->result;
@ -64,51 +60,51 @@ Simple Database Class (C) by Bastian Schroll
/** /**
* Database::fetchAssoc() * Database::fetchAssoc()
* *
* Liefert alle gefundnen Datensaetze als Assoc * Liefert alle gefundnen Datens<EFBFBD>tze als Assoc
* *
* @param mixed $result Externer Result-Handler * @param mixed $result Externer Result-Handler
* @return gefundene Datensaetze als Assoc * @return gefundene Datens<EFBFBD>tze als Assoc
*/ */
function fetchAssoc($result = null) function fetchAssoc($result = null)
{ {
if ($result != null) if ($result != null)
{ {
return @mysqli_fetch_assoc($result); return @mysql_fetch_assoc($result);
} else } else
{ {
return @mysqli_fetch_assoc($this->result); return @mysql_fetch_assoc($this->result);
} }
} }
/** /**
* Database::count() * Database::count()
* *
* Zaehlt alle gefundenen Datensaetze * Z<EFBFBD>hlt alle gefundenen Datens<EFBFBD>tze
* *
* @param mixed $result Externer Result-Handler * @param mixed $result Externer Result-Handler
* @return Anzahl gefundener Datensaetze * @return Anzahl gefundener Datens<EFBFBD>tze
*/ */
function count($result = null) function count($result = null)
{ {
if ($result != null) if ($result != null)
{ {
return @mysqli_num_rows($result); return @mysql_num_rows($result);
} else } else
{ {
return @mysqli_num_rows($this->result); return @mysql_num_rows($this->result);
} }
} }
/** /**
* Database::closeConnection() * Database::closeConnection()
* *
* Schliesst die bestehende MySQL Verbindung * Schlie<EFBFBD>t die bestehende MySQL Verbindung
* *
* @return TRUE/FALSE * @return TRUE/FALSE
*/ */
function closeConnection() function closeConnection()
{ {
if (!@mysqli_close($this->conn)) if (!@mysql_close($this->conn))
{ {
$this->error("Verbindung zur Datenbank konnte nicht getrennt werden!", mysql_error()); $this->error("Verbindung zur Datenbank konnte nicht getrennt werden!", mysql_error());
return false; return false;

73
includes/NMAHandler.py Normal file
View file

@ -0,0 +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 "<module>" if the message is not in a function
if len(record.funcName) > 0:
if not record.funcName == "<module>":
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)

View file

@ -14,7 +14,6 @@ import logging # Global logger
import time # timestamp import time # timestamp
from includes import globalVars # Global variables from includes import globalVars # Global variables
from copy import deepcopy # copy objects to avoid issues if the objects will be changed by the plugin's during runtime and during asynch/threaded processing
## ##
# #
@ -41,7 +40,7 @@ def processAlarmHandler(typ, freq, data):
logging.debug("starting processAlarm async") logging.debug("starting processAlarm async")
try: try:
from threading import Thread from threading import Thread
Thread(target=processAlarm, args=(typ, freq, deepcopy(data))).start() Thread(target=processAlarm, args=(typ, freq, data)).start()
except: except:
logging.error("Error in starting alarm processing async") logging.error("Error in starting alarm processing async")
logging.debug("Error in starting alarm processing async", exc_info=True) logging.debug("Error in starting alarm processing async", exc_info=True)
@ -81,7 +80,7 @@ def processAlarm(typ, freq, data):
if regexFilter.checkFilters(typ, data, pluginName, freq): if regexFilter.checkFilters(typ, data, pluginName, freq):
logging.debug("call Plugin: %s", pluginName) logging.debug("call Plugin: %s", pluginName)
try: try:
plugin.run(typ, freq, deepcopy(data)) plugin.run(typ, freq, data)
logging.debug("return from: %s", pluginName) logging.debug("return from: %s", pluginName)
except: except:
# call next plugin, if one has thrown an exception # call next plugin, if one has thrown an exception
@ -89,7 +88,7 @@ def processAlarm(typ, freq, data):
else: # RegEX filter off - call plugin directly else: # RegEX filter off - call plugin directly
logging.debug("call Plugin: %s", pluginName) logging.debug("call Plugin: %s", pluginName)
try: try:
plugin.run(typ, freq, deepcopy(data)) plugin.run(typ, freq, data)
logging.debug("return from: %s", pluginName) logging.debug("return from: %s", pluginName)
except: except:
# call next plugin, if one has thrown an exception # call next plugin, if one has thrown an exception

View file

@ -33,7 +33,7 @@ def decode(freq, decoded):
# ZVEI Decoder Section # ZVEI Decoder Section
# check ZVEI: -> validate -> check double alarm -> log # check ZVEI: -> validate -> check double alarm -> log
elif "ZVEI1:" in decoded: elif "ZVEI2:" in decoded:
logging.debug("received ZVEI") logging.debug("received ZVEI")
from includes.decoders import zvei from includes.decoders import zvei
zvei.decode(freq, decoded) zvei.decode(freq, decoded)

View file

@ -34,7 +34,6 @@ def isAllowed(poc_id):
""" """
allowed = 0 allowed = 0
has_geo = False
# 1.) If allowed RICs is set, only they will path, # 1.) If allowed RICs is set, only they will path,
# If RIC is the right one return True, else False # If RIC is the right one return True, else False
@ -63,23 +62,11 @@ def isAllowed(poc_id):
return True return True
else: else:
allowed = 0 allowed = 0
# 5.) Implementation for multicastAlarm
if globalVars.config.get("multicastAlarm", "multicastAlarm_delimiter_ric"):
if poc_id in globalVars.config.get("multicastAlarm", "multicastAlarm_delimiter_ric"):
logging.info("RIC %s as multicastAlarm delimiter", poc_id)
return True
else:
allowed = 0
if globalVars.config.get("multicastAlarm", "multicastAlarm_ric"):
if poc_id in globalVars.config.get("multicastAlarm", "multicastAlarm_ric"):
logging.info("RIC %s as multicastAlarm message", poc_id)
return True
else:
allowed = 0
if allowed == 0: if allowed == 0:
return False return False
return True return True
## ##
# #
# POCSAG decoder function # POCSAG decoder function
@ -99,8 +86,6 @@ def decode(freq, decoded):
@return: nothing @return: nothing
@exception: Exception if POCSAG decode failed @exception: Exception if POCSAG decode failed
""" """
has_geo = False
try: try:
bitrate = 0 bitrate = 0
@ -127,64 +112,21 @@ def decode(freq, decoded):
if "Alpha:" in decoded: #check if there is a text message if "Alpha:" in decoded: #check if there is a text message
poc_text = decoded.split('Alpha: ')[1].strip().replace('<NUL><NUL>','').replace('<NUL>','').replace('<NUL','').replace('< NUL>','').replace('<EOT>','').strip() poc_text = decoded.split('Alpha: ')[1].strip().replace('<NUL><NUL>','').replace('<NUL>','').replace('<NUL','').replace('< NUL>','').replace('<EOT>','').strip()
if globalVars.config.getint("POC","geo_enable"):
try:
logging.debug("Using %s to find geo-tag in %s", globalVars.config.get("POC","geo_format"),poc_text)
m = re.search(globalVars.config.get("POC","geo_format"),poc_text)
if m:
logging.debug("Found geo-tag in message, parsing...")
has_geo = True
geo_order = globalVars.config.get("POC","geo_order").split(',')
if geo_order[0].lower == "lon":
lat = m.group(1) + "." + m.group(2)
lon = m.group(3) + "." + m.group(4)
else:
lon = m.group(1) + "." + m.group(2)
lat = m.group(3) + "." + m.group(4)
logging.debug("Finished parsing geo; lon: %s, lat: %s", lon, lat)
else:
logging.debug("No geo-tag found")
has_geo = False
except:
has_geo = False
logging.error("Exception parsing geo-information",exc_info=true)
else:
has_geo = False
else: else:
poc_text = "" poc_text = ""
if re.search("[0-9]{7}", poc_id) and re.search("[1-4]{1}", poc_sub): #if POC is valid if re.search("[0-9]{7}", poc_id) and re.search("[1-4]{1}", poc_sub): #if POC is valid
if isAllowed(poc_id): if isAllowed(poc_id):
# check for double alarm # check for double alarm
if doubleFilter.checkID("POC", poc_id+poc_sub, poc_text): if doubleFilter.checkID("POC", poc_id+poc_sub, poc_text):
data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate, "description":poc_id, "has_geo":has_geo} logging.info("POCSAG%s: %s %s %s ", bitrate, poc_id, poc_sub, poc_text)
if has_geo == True: data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate, "description":poc_id}
data["lon"] = lon
data["lat"] = lat
# Add function as character a-d to dataset # Add function as character a-d to dataset
data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d")
data["ricFuncChar"] = data["ric"] + data["functionChar"]
logging.info("POCSAG%s: %s %s %s ", data["bitrate"], data["ric"], data["function"], data["msg"])
# If enabled, look up description # If enabled, look up description
if globalVars.config.getint("POC", "idDescribed"): if globalVars.config.getint("POC", "idDescribed"):
from includes import descriptionList from includes import descriptionList
data["description"] = descriptionList.getDescription("POC", data["ric"]+data["functionChar"]) data["description"] = descriptionList.getDescription("POC", poc_id)
# multicastAlarm processing if enabled and a message without text or delimiter RIC or netIdent_ric received
if globalVars.config.getint("multicastAlarm", "multicastAlarm") and data["ric"] != globalVars.config.get("POC", "netIdent_ric") and (data["msg"] == "" or data["ric"] in globalVars.config.get("multicastAlarm", "multicastAlarm_delimiter_ric")):
logging.debug(" - multicastAlarm without msg")
from includes import multicastAlarm
multicastAlarm.newEntrymultiList(data)
# multicastAlarm processing if enabled and alarm message has been received
elif globalVars.config.getint("multicastAlarm", "multicastAlarm") and data["msg"] != "" and data["ric"] in globalVars.config.get("multicastAlarm", "multicastAlarm_ric"):
logging.debug(" - multicastAlarm with message")
from includes import multicastAlarm
multicastAlarm.multicastAlarmExec(freq, data)
else:
# processing the alarm # processing the alarm
try: try:
from includes import alarmHandler from includes import alarmHandler

View file

@ -17,24 +17,24 @@ from includes import doubleFilter # double alarm filter
## ##
# #
# Local function to remove the 'E' # Local function to remove the 'F'
# #
def removeE(zvei): def removeF(zvei):
""" """
Resolve the E from the repeat Tone Resolve the F from the repeat Tone
@type zvei: string @type zvei: string
@param zvei: ZVEI Information @param zvei: ZVEI Information
@return: ZVEI without E @return: ZVEI without F
@exception: none @exception: none
""" """
if "E" in zvei: if "F" in zvei:
zvei_old = zvei zvei_old = zvei
for i in range(1, len(zvei_old)): for i in range(1, len(zvei_old)):
if zvei[i] == "E": if zvei[i] == "F":
zvei = zvei.replace("E",zvei[i-1],1) zvei = zvei.replace("F",zvei[i-1],1)
logging.debug("remove E: %s -> %s", zvei_old, zvei) logging.debug("resolve F: %s -> %s", zvei_old, zvei)
return zvei return zvei
## ##
@ -58,7 +58,7 @@ def decode(freq, decoded):
""" """
try: try:
zvei_id = decoded[7:12] # ZVEI Code zvei_id = decoded[7:12] # ZVEI Code
zvei_id = removeE(zvei_id) # remove E (repeated tone) zvei_id = removeF(zvei_id) # resolve F
if re.search("[0-9]{5}", zvei_id): # if ZVEI is valid if re.search("[0-9]{5}", zvei_id): # if ZVEI is valid
# check for double alarm # check for double alarm
if doubleFilter.checkID("ZVEI", zvei_id): if doubleFilter.checkID("ZVEI", zvei_id):

View file

@ -11,7 +11,6 @@ Function to expand the dataset with a description.
import logging # Global logger import logging # Global logger
import csv # for loading the description files import csv # for loading the description files
import re # for matching IDs with a regular expression
from includes import globalVars # Global variables from includes import globalVars # Global variables
from includes.helper import stringConverter from includes.helper import stringConverter
@ -42,10 +41,10 @@ def loadCSV(typ, idField):
reader = csv.DictReader(csvfile) reader = csv.DictReader(csvfile)
for row in reader: for row in reader:
logging.debug(row) logging.debug(row)
# only import rows with an integer as id, allow subrics though # only import rows with an integer as id
if re.match("^[0-9A-F]+$", row[idField], re.IGNORECASE): if row[idField].isdigit() == True:
try: try:
resultList[row[idField].lower()] = stringConverter.convertToUTF8(row['description']) resultList[row[idField]] = stringConverter.convertToUTF8(row['description'])
except: except:
# skip entry in case of an exception # skip entry in case of an exception
pass pass
@ -102,19 +101,18 @@ def getDescription(typ, data):
@return: description as string @return: description as string
""" """
resultStr = data resultStr = data;
logging.debug("look up description lists") logging.debug("look up description lists")
try: try:
if typ == "FMS": if typ == "FMS":
global fmsDescribtionList
resultStr = fmsDescribtionList[data] resultStr = fmsDescribtionList[data]
elif typ == "ZVEI": elif typ == "ZVEI":
global zveiDescribtionList
resultStr = zveiDescribtionList[data] resultStr = zveiDescribtionList[data]
elif typ == "POC": elif typ == "POC":
if globalVars.config.getint("POC", "onlysubric"): global ricDescribtionList
resultStr = ricDescribtionList[data] # only SubRIC resultStr = ricDescribtionList[data]
else:
resultStr = ricDescribtionList[data[:-1]] # MainRIC
resultStr += " " + ricDescribtionList[data] # SubRIC
else: else:
logging.warning("Invalid Typ: %s", typ) logging.warning("Invalid Typ: %s", typ)

View file

@ -31,10 +31,12 @@ def checkID(typ, data, msg=""):
@return: True if check was OK @return: True if check was OK
@return: False if double was found @return: False if double was found
""" """
global doubleList
timestamp = int(time.time()) # Get Timestamp timestamp = int(time.time()) # Get Timestamp
logging.debug("checkID: %s (%s)", data, msg) logging.debug("checkID: %s (%s)", data, msg)
for (xID, xTimestamp, xMsg) in doubleList: for i in range(len(doubleList)):
(xID, xTimestamp, xMsg) = doubleList[i]
# given ID found? # given ID found?
# return False if the first entry in double_ignore_time is found, we will not check for younger ones... # return False if the first entry in double_ignore_time is found, we will not check for younger ones...
if data == xID and timestamp < xTimestamp + globalVars.config.getint("BOSWatch", "doubleFilter_ignore_time"): if data == xID and timestamp < xTimestamp + globalVars.config.getint("BOSWatch", "doubleFilter_ignore_time"):

View file

@ -9,9 +9,8 @@ Global variables
""" """
# version info # version info
versionNr = "2.5.2" versionNr = "2.2-beta"
branch = "master" buildDate = "2016/02/23"
buildDate = "08.01.2021"
# Global variables # Global variables
config = 0 config = 0

View file

@ -159,6 +159,7 @@ def convertToUTF8(inputString = ""):
raise raise
# End of exception UnicodeDecodeError: check given string is already UTF-8 # End of exception UnicodeDecodeError: check given string is already UTF-8
pass
except: except:
logging.warning("error checking given string") logging.warning("error checking given string")

View file

@ -17,7 +17,7 @@ from includes import globalVars
from includes.helper import timeHandler from includes.helper import timeHandler
def replaceWildcards(text, data): def replaceWildcards(text, data, lineBrakeAllowed=False):
""" """
Replace all official Wildcards with the Information from the data[] var Replace all official Wildcards with the Information from the data[] var
@ -25,6 +25,8 @@ def replaceWildcards(text, data):
@param text: Input text with wildcards @param text: Input text with wildcards
@type data: map @type data: map
@param data: map of data (structure see readme.md in plugin folder) @param data: map of data (structure see readme.md in plugin folder)
@type lineBrakeAllowed: Boolean
@param lineBrakeAllowed: switch to allow lineBreak (%BR%) as wildcard
@return: text with replaced wildcards @return: text with replaced wildcards
@exception: Exception if Error at replace @exception: Exception if Error at replace
@ -34,6 +36,7 @@ def replaceWildcards(text, data):
text = text.replace("%TIME%", timeHandler.getTime(data["timestamp"])).replace("%DATE%", timeHandler.getDate(data["timestamp"])) text = text.replace("%TIME%", timeHandler.getTime(data["timestamp"])).replace("%DATE%", timeHandler.getDate(data["timestamp"]))
# replace some special chars # replace some special chars
if lineBrakeAllowed == True:
text = text.replace("%BR%", "\r\n") text = text.replace("%BR%", "\r\n")
text = text.replace("%LPAR%", "(") text = text.replace("%LPAR%", "(")
text = text.replace("%RPAR%", ")") text = text.replace("%RPAR%", ")")

View file

@ -1,65 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
multicastAlarm is the function to enable BOSwatch to work in networks that optimise the transmission of POCSAG telegrams
@author: Fabian Kessler
@requires: Configuration has to be set in the config.ini
"""
import logging # Global logger
import time # timestamp for multicastAlarm
from includes import globalVars # Global variables
#
# ListStructure [0..n] = (Data, TimeStamp)
#
multiList = []
def newEntrymultiList(data):
"""
add entry to multi alarm list and remove old entries
@return: nothing
"""
global multiList
timestamp = int(time.time())
# multicastAlarm processing if enabled and delimiter RIC has been received
if data['ric'] == globalVars.config.get("multicastAlarm", "multicastAlarm_delimiter_ric"):
del multiList[:]
logging.debug("delimiter RIC received - buffer cleared")
else:
multiList.append([data, timestamp])
logging.debug("Added %s to multiList", data['ric'])
# check for old entries in multiList
for (xData, xTimestamp) in multiList[:]:
if xTimestamp < timestamp-globalVars.config.getint("multicastAlarm", "multicastAlarm_ignore_time"):
multiList.remove([xData, xTimestamp])
logging.debug("RIC %s removed - %s sec. older than current timestamp", xData['ric'], xTimestamp-timestamp)
def multicastAlarmExec(freq, data):
"""
call alarmHandler for every entry in multiList
@return: nothing
"""
logging.debug("data before update from multiList: %s", data)
for (xData, _) in multiList:
#update data with values multiList
data['ric'] = xData['ric']
data['function'] = xData['function']
data['functionChar'] = xData['functionChar']
data['description'] = xData['description']
logging.debug("data after update from multiList: %s", data)
try:
from includes import alarmHandler
alarmHandler.processAlarmHandler("POC", freq, data)
except:
logging.error("processing alarm failed")
logging.debug("processing alarm failed", exc_info=True)
del multiList[:]
logging.debug("multicastAlarm finished - buffer cleared")

137
includes/pynma/README.md Normal file
View file

@ -0,0 +1,137 @@
Pynma
======
Pynma is a simple python module for the [NotifyMyAndroid][nma] [API][NMA API].
[nma]: http://nma.usk.bz/
[NMA API]: http://nma.usk.bz/api.php
Credits to: Damien Degois (github.com/babs)
Refactoring: Adriano Maia (adriano@usk.bz)
[NotifyMyAndroid][nma]
---------------
NotifyMyAndroid is a Prowl-like application for the Android. Notifications can be sent from your application Android device using push. NMA has an extensive API, which allows your scripts to integrate beautifully. (source: http://nma.usk.bz/)
### How it works:
First, import the module:
import pynma
#### Keys management
Create a PyNMA simple instance:
p = pynma.PyNMA( "apikey(s)", "developerkey")
A developerkey is optional. If you'd like to add just one API key, set it as string, if you want more, just provide a list of API key strings.
p = pynma.PyNMA(['apikey1','apikey2']) # multiple API keys
p = pynma.PyNMA("apikey1","providerkey") # 1 API key with a providerkey
For more flexible usage, you can add and remove keys:
p.addkey("apikey1")
p.addkey(["apikey2","apikey3"])
Or set or change the providerkey
p.developerkey("developerkey")
#### Notification or Push or Add
p.push(application, event, description, (opt) url, (opt) priority, (opt) batch mode)
##### Application
Application is your message generating application name (limited to 256)
ex: my music player
##### Event
Event is the event name (limited to 1000)
ex: switched to next track
##### Description
The description is the payload of your message (limited to 10000 (10k))
ex:
Playing next song, Blah Blah Blah
Artist: blah blah
Album: blah blah
Track: 18/24
##### Url
The URL which should be attached to the notification.
This will trigger a redirect when on the user's device launched, and is viewable in the notification list.
##### Priority
Priority goes from -2 (lowest) to 2 (highest). the default priority is 0 (normal)
##### Batch mode
Batch mode is a boolean value to set if you'd like to push the same message to multiple API keys 5 by 5 (as the actual verion of prowl API allows you). This can reduce the number of call you make to the API which are limited.
#### Return
The push method returns a dict containing different values depending of the success of you call:
##### The call succeed
you'll have in the dict those keys:
type: success
code: the HTTP like code (200 if success)
remaining: the number of API call you can to until the reset
resetdate: number of remaining minutes till the hourly reset of your API call limit
##### The call failed
For wathever reason, you call failed, the dict key "message" will contains the erro message returned by Prowl API. You'll find those keys:
code: 400, 401, 402 or 500 (depends of the error kind)
message: API error message
For the code description, please refer to [NMA API documentation][NMA API] for more informations
##### The python module encountered an unhandled problem (mostly during parsing)
The return keys will be:
code: 600
type: pynmaerror
message: the exception message
Thanks
------
* **Cev** for URL integration and some fixes in docstring
* **ChaoticXSinZ** for UTF-8 integration and other typos
License (MIT)
-------------
Copyright (c) 2010-2011, Damien Degois.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,3 @@
#!/usr/bin/python
from .pynma import PyNMA

153
includes/pynma/pynma.py Normal file
View file

@ -0,0 +1,153 @@
#!/usr/bin/python
from xml.dom.minidom import parseString
try:
from http.client import HTTPSConnection
except ImportError:
from httplib import HTTPSConnection
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
__version__ = "1.0"
API_SERVER = 'www.notifymyandroid.com'
ADD_PATH = '/publicapi/notify'
USER_AGENT="PyNMA/v%s"%__version__
def uniq_preserve(seq): # Dave Kirby
# Order preserving
seen = set()
return [x for x in seq if x not in seen and not seen.add(x)]
def uniq(seq):
# Not order preserving
return list({}.fromkeys(seq).keys())
class PyNMA(object):
"""PyNMA(apikey=[], developerkey=None)
takes 2 optional arguments:
- (opt) apykey: might me a string containing 1 key or an array of keys
- (opt) developerkey: where you can store your developer key
"""
def __init__(self, apikey=[], developerkey=None):
self._developerkey = None
self.developerkey(developerkey)
if apikey:
if type(apikey) == str:
apikey = [apikey]
self._apikey = uniq(apikey)
def addkey(self, key):
"Add a key (register ?)"
if type(key) == str:
if not key in self._apikey:
self._apikey.append(key)
elif type(key) == list:
for k in key:
if not k in self._apikey:
self._apikey.append(k)
def delkey(self, key):
"Removes a key (unregister ?)"
if type(key) == str:
if key in self._apikey:
self._apikey.remove(key)
elif type(key) == list:
for k in key:
if key in self._apikey:
self._apikey.remove(k)
def developerkey(self, developerkey):
"Sets the developer key (and check it has the good length)"
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:
- (req) application: application name [256]
- (req) event: event name [1000]
- (req) description: description [10000]
- (opt) url: url [512]
- (opt) contenttype: Content Type (act: None (plain text) or text/html)
- (opt) priority: from -2 (lowest) to 2 (highest) (def:0)
- (opt) batch_mode: push to all keys at once (def:False)
- (opt) html: shortcut for contenttype=text/html
Warning: using batch_mode will return error only if all API keys are bad
cf: http://nma.usk.bz/api.php
"""
datas = {
'application': application[:256].encode('utf8'),
'event': event[:1024].encode('utf8'),
'description': description[:10000].encode('utf8'),
'priority': priority
}
if url:
datas['url'] = url[:512]
if contenttype == "text/html" or html == True: # Currently only accepted content type
datas['content-type'] = "text/html"
if self._developerkey:
datas['developerkey'] = self._developerkey
results = {}
if not batch_mode:
for key in self._apikey:
datas['apikey'] = key
res = self.callapi('POST', ADD_PATH, datas)
results[key] = res
else:
datas['apikey'] = ",".join(self._apikey)
res = self.callapi('POST', ADD_PATH, datas)
results[datas['apikey']] = res
return results
def callapi(self, method, path, args):
headers = { 'User-Agent': USER_AGENT }
if method == "POST":
headers['Content-type'] = "application/x-www-form-urlencoded"
http_handler = HTTPSConnection(API_SERVER)
http_handler.request(method, path, urlencode(args), headers)
resp = http_handler.getresponse()
try:
res = self._parse_reponse(resp.read())
except Exception as e:
res = {'type': "pynmaerror",
'code': 600,
'message': str(e)
}
return res
def _parse_reponse(self, response):
root = parseString(response).firstChild
for elem in root.childNodes:
if elem.nodeType == elem.TEXT_NODE: continue
if elem.tagName == 'success':
res = dict(list(elem.attributes.items()))
res['message'] = ""
res['type'] = elem.tagName
return res
if elem.tagName == 'error':
res = dict(list(elem.attributes.items()))
res['message'] = elem.firstChild.nodeValue
res['type'] = elem.tagName
return res

View file

@ -28,6 +28,7 @@ def loadFilters():
@return: nothing @return: nothing
""" """
global filterList
try: try:
logging.debug("loading filters") logging.debug("loading filters")
# For each entry in config.ini [Filters] section # For each entry in config.ini [Filters] section
@ -88,7 +89,7 @@ def checkFilters(typ, data, plugin, freq):
return False return False
else: else:
logging.debug("no Filter found") logging.debug("no Filter found")
return False return True
except: except:
logging.error("Error in filter checking") logging.error("Error in filter checking")

View file

@ -31,7 +31,6 @@ def printHeader(args):
print " by Bastian Schroll, Jens Herrmann " print " by Bastian Schroll, Jens Herrmann "
print "" print ""
print "SW Version: "+globalVars.versionNr print "SW Version: "+globalVars.versionNr
print "Branch: "+globalVars.branch
print "Build Date: "+globalVars.buildDate print "Build Date: "+globalVars.buildDate
print "" print ""

231
install.sh Executable file → Normal file
View file

@ -6,17 +6,17 @@ function exitcodefunction {
module=$3 module=$3
if [ $errorcode -ne "0" ]; then if [ $errorcode -ne "0" ]; then
echo "Action: $action on $module failed." >> $boswatch_install_path/setup_log.txt echo "Action: $action on $module failed." >> $boswatchpath/install/setup_log.txt
echo "Exitcode: $errorcode" >> $boswatch_install_path/setup_log.txt echo "Exitcode: $errorcode" >> $boswatchpath/install/setup_log.txt
echo "" echo ""
echo "Action: $action on $module failed." echo "Action: $action on $module failed."
echo "Exitcode: $errorcode" echo "Exitcode: $errorcode"
echo "" echo ""
echo " -> If you want to open an issue at https://github.com/Schrolli91/BOSWatch/issues" echo " -> If you want to open an Issue at https://github.com/Schrolli91/BOSWatch/issues"
echo " please post the logfile, located at $boswatch_install_path/setup_log.txt" echo " please post the logfile, located at $boswatchpath/install/setup_log.txt"
exit 1 exit 1
else else
echo "Action: $action on $module ok." >> $boswatch_install_path/setup_log.txt echo "Action: $action on $module ok." >> $boswatchpath/install/setup_log.txt
fi fi
} }
@ -44,32 +44,8 @@ echo "Caution, script does not install a webserver with PHP and MySQL"
echo "So you have to make up manually if you want to use MySQL support" echo "So you have to make up manually if you want to use MySQL support"
boswatchpath=/opt/boswatch boswatchpath=/opt/boswatch
boswatch_install_path=/opt/boswatch_install
reboot=false reboot=false
didBackup=false
# Checking for Backup
# check for old version (for the old ones...)
if [ -f $boswatchpath/BOSWatch/boswatch.py ]; then
echo "Old installation found!"
echo "A backup will be copied to $boswatchpath/old"
mkdir /tmp/boswatch
mv $boswatchpath/BOSWatch/* /tmp/boswatch/
didBackup=true
fi
#and the future...
if [ -f $boswatchpath/boswatch.py ]; then
echo "Old installation found!"
echo "A backup will be copied to $boswatchpath/old"
mkdir /tmp/boswatch
mv $boswatchpath/* /tmp/boswatch/
didBackup=true
fi
# Check for Flags in command line
for (( i=1; i<=$#; i=$i+2 )); do for (( i=1; i<=$#; i=$i+2 )); do
t=$((i + 1)) t=$((i + 1))
eval arg=\$$i eval arg=\$$i
@ -81,149 +57,154 @@ for (( i=1; i<=$#; i=$i+2 )); do
-b|--branch) -b|--branch)
case $arg2 in case $arg2 in
dev|develop) echo " !!! WARNING: you are using the DEV BRANCH !!! "; branch=dev ;; dev|develop) echo " !!! WARNING: you are using the DEV BRANCH !!! "; branch=dev ;;
beta) echo " !!! WARNING: you are using the BETA BRANCH !!! "; branch=beta ;;
*) branch=master ;; *) branch=master ;;
esac ;; esac ;;
-p|--path) echo " !!! WARNING: you'll install BOSWATCH to alternative path !!! "; boswatchpath=$arg2 ;; -p|--path) echo " !!! WARNING: you install BOSWATCH to alternative path !!! "; boswatchpath=$arg2 ;;
*) echo "Internal error!" ; exit 1 ;; *) echo "Internal error!" ; exit 1 ;;
esac esac
done done
# Create default paths
mkdir -p $boswatchpath mkdir -p $boswatchpath
mkdir -p $boswatch_install_path mkdir -p $boswatchpath/install
echo "" echo ""
# Update of computer
tput cup 13 15 tput cup 13 15
echo "[ 1/9] [#--------]" echo "[ 1/10] [#---------]"
tput cup 15 5 tput cup 15 5
echo "-> make an apt-get update................" echo "-> make an apt-get update................"
apt-get update -y > $boswatch_install_path/setup_log.txt 2>&1 apt-get update -y > $boswatchpath/install/setup_log.txt 2>&1
# download software
tput cup 13 15 tput cup 13 15
echo "[ 2/9] [##-------]" echo "[ 2/10] [##--------]"
tput cup 15 5 tput cup 15 5
echo "-> download GIT and other stuff.........." echo "-> download GIT and other stuff.........."
apt-get -y install git cmake build-essential libusb-1.0 qt4-qmake qt4-default libpulse-dev libx11-dev sox python-pip >> $boswatch_install_path/setup_log.txt 2>&1 apt-get -y install git cmake build-essential libusb-1.0 qt4-qmake qt4-default libpulse-dev libx11-dev sox >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? download stuff exitcodefunction $? download stuff
# download BOSWatch via git
tput cup 13 15 tput cup 13 15
echo "[ 3/9] [###------]" echo "[ 3/10] [###-------]"
tput cup 15 5
echo "-> download rtl_fm......................"
cd $boswatchpath/install
git clone https://github.com/Schrolli91/rtl-sdr.git >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? git-clone rtl-sdr
cd rtl-sdr/
tput cup 13 15
echo "[ 4/10] [####------]"
tput cup 15 5
echo "-> compile rtl_fm......................"
mkdir -p build && cd build
cmake ../ -DINSTALL_UDEV_RULES=ON >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? cmake rtl-sdr
make >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? make rtl-sdr
make install >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? make-install rtl-sdr
ldconfig >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? ldconfig rtl-sdr
tput cup 13 15
echo "[ 5/10] [#####-----]"
tput cup 15 5
echo "-> download multimon-ng................"
cd $boswatchpath/install
git clone https://github.com/Schrolli91/multimon-ng.git multimonNG >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? git-clone multimonNG
cd $boswatchpath/install/multimonNG/
tput cup 13 15
echo "[ 6/10] [######----]"
tput cup 15 5
echo "-> compile multimon-ng................."
mkdir -p build
cd build
qmake ../multimon-ng.pro >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? qmake multimonNG
make >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? make multimonNG
make install >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? qmakeinstall multimonNG
tput cup 13 15
echo "[ 7/10] [#######---]"
tput cup 15 5
echo "-> download MySQL connector for Python."
cd $boswatchpath/install
wget "http://dev.mysql.com/get/Downloads/Connector-Python/mysql-connector-python-1.0.9.tar.gz/from/http://cdn.mysql.com/" -O mysql-connector.tar >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? download mysql-connector
tar xfv mysql-connector.tar >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? untar mysql-connector
cd $boswatchpath/install/mysql-connector-python*
tput cup 13 15
echo "[ 8/10] [########--]"
tput cup 15 5
echo "-> install MySQL connector for Python.."
chmod +x ./setup.py
./setup.py install >> $boswatchpath/install/setup_log.txt 2>&1
exitcodefunction $? setup mysql-connector
tput cup 13 15
echo "[ 9/10] [#########-]"
tput cup 15 5 tput cup 15 5
echo "-> download BOSWatch..................." echo "-> download BOSWatch..................."
cd $boswatchpath/ cd $boswatchpath/
case $branch in case $branch in
"dev") git clone -b develop https://github.com/Schrolli91/BOSWatch . >> $boswatch_install_path/setup_log.txt 2>&1 && \ "dev") git clone -b develop https://github.com/Schrolli91/BOSWatch >> $boswatchpath/install/setup_log.txt 2>&1 && \
exitcodefunction $? git-clone BOSWatch-develop ;; exitcodefunction $? git-clone BOSWatch-develop ;;
*) git clone -b master https://github.com/Schrolli91/BOSWatch . >> $boswatch_install_path/setup_log.txt 2>&1 && \ "beta") git clone -b beta https://github.com/Schrolli91/BOSWatch >> $boswatchpath/install/setup_log.txt 2>&1 && \
exitcodefunction $? git-clone BOSWatch-beta ;;
*) git clone -b master https://github.com/Schrolli91/BOSWatch >> $boswatchpath/install/setup_log.txt 2>&1 && \
exitcodefunction $? git-clone BOSWatch ;; exitcodefunction $? git-clone BOSWatch ;;
esac esac
# Download RTL-SDR
tput cup 13 15 tput cup 13 15
echo "[ 4/9] [####-----]" echo "[10/10] [##########]"
tput cup 15 5
echo "-> download rtl_fm......................"
cd $boswatch_install_path
git clone https://github.com/Schrolli91/rtl-sdr.git >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? git-clone rtl-sdr
cd rtl-sdr/
# Compie RTL-FM
tput cup 13 15
echo "[ 5/9] [#####----]"
tput cup 15 5
echo "-> compile rtl_fm......................"
mkdir -p build && cd build
cmake ../ -DINSTALL_UDEV_RULES=ON >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? cmake rtl-sdr
make >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? make rtl-sdr
make install >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? make-install rtl-sdr
ldconfig >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? ldconfig rtl-sdr
# Download Multimon-NG
tput cup 13 15
echo "[ 6/9] [######---]"
tput cup 15 5
echo "-> download multimon-ng................"
cd $boswatch_install_path
git clone https://github.com/Schrolli91/multimon-ng.git multimonNG >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? git-clone multimonNG
cd $boswatch_install_path/multimonNG/
# Compile Multimon-NG
tput cup 13 15
echo "[ 7/9] [#######--]"
tput cup 15 5
echo "-> compile multimon-ng................."
mkdir -p build
cd build
qmake ../multimon-ng.pro >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? qmake multimonNG
make >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? make multimonNG
make install >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? qmakeinstall multimonNG
# Download & Install MySQL-Connector for Python via pip
tput cup 13 15
echo "[ 8/9] [########-]"
tput cup 15 5
echo "-> Download & Install MySQL connector for Python."
cd $boswatch_install_path
pip install mysql-connector-python >> $boswatch_install_path/setup_log.txt 2>&1
exitcodefunction $? install mysql-connector
# Blacklist DVB-Drivers
tput cup 13 15
echo "[9/9] [#########]"
tput cup 15 5 tput cup 15 5
echo "-> configure..........................." echo "-> configure..........................."
cd $boswatchpath/ cd $boswatchpath/
echo $'# BOSWatch - blacklist the DVB drivers to avoid conflicts with the SDR driver\n blacklist dvb_usb_rtl28xxu \n blacklist rtl2830\n blacklist dvb_usb_v2\n blacklist dvb_core' >> /etc/modprobe.d/boswatch_blacklist_sdr.conf chmod +x *
echo $'# BOSWatch - blacklist the DVB drivers to avoid conflict with the SDR driver\n blacklist dvb_usb_rtl28xxu \n blacklist rtl2830\n blacklist dvb_usb_v2\n blacklist dvb_core' >> /etc/modprobe.d/boswatch_blacklist_sdr.conf
# Installation is ready
tput cup 17 1 tput cup 17 1
echo "BOSWatch is now installed in $boswatchpath/" echo "BOSWatch is now installed in $boswatchpath/"
echo "Installation ready!" echo "Installation ready!"
tput cup 19 3 tput cup 19 3
echo "Watch out: to run BOSWatch you have to modify the config.ini!" echo "Watch out: to run BOSWatch you have to generate and modify the config.ini!"
echo "Do the following step to do so:" echo "Do the following steps to have a running version of BOSWatch:"
echo "sudo nano $boswatchpath/config/config.ini" echo "sudo cp $boswatchpath/BOSWatch/config/config.template.ini $boswatchpath/BOSWatch/config/config.ini"
echo "sudo nano $boswatchpath/BOSWatch/config/config.ini"
echo "and modify the config as you need. This step is optional if you are upgrading an old version of BOSWatch. " echo "and modify the config as you need. This step is optional if you are upgrading an old version of BOSWatch. "
tput cnorm tput cnorm
# cleanup # cleanup
mkdir $boswatchpath/log/install -p mkdir $boswatchpath/log/install -p
mv $boswatch_install_path/setup_log.txt $boswatchpath/log/install/ mv $boswatchpath/install/setup_log.txt $boswatchpath/log/install/
rm $boswatch_install_path/ -R rm $boswatchpath/install/ -R
#copy the template config to run boswatch mv $boswatchpath/BOSWatch/* $boswatchpath/
cp $boswatchpath/config/config.template.ini $boswatchpath/config/config.ini rm $boswatchpath/BOSWatch -R
#replay the backup
if [ $didBackup = "true" ]; then
mkdir $boswatchpath/old/
mv /tmp/boswatch/* $boswatchpath/old/
fi
if [ $reboot = "true" ]; then if [ $reboot = "true" ]; then
/sbin/reboot /sbin/reboot

View file

@ -1,241 +0,0 @@
#!/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
def isSignal(poc_id):
"""
@type poc_id: string
@param poc_id: POCSAG Ric
@requires: Configuration has to be set in the config.ini
@return: True if the Ric is Signal, other False
@exception: none
"""
# If RIC is Signal return True, else False
if globalVars.config.get("POC", "netIdent_ric"):
if poc_id in globalVars.config.get("POC", "netIdent_ric"):
logging.info("RIC %s is net ident", poc_id)
return True
else:
logging.info("RIC %s is no net ident", poc_id)
return False
##
#
# 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")
vehicle = globalVars.config.get("Divera", "fms_vehicle")
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_prio")
zvei_id = globalVars.config.get("Divera","zvei_id")
elif typ == "POC":
if isSignal(data["ric"]):
logging.debug("RIC is net ident")
return
else:
#
# 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")
ric = globalVars.config.get("Divera", "poc_ric")
else:
logging.warning("Invalid type: %s", typ)
return
try:
#
# Divera-Request
#
logging.debug("send Divera for %s", typ)
# Replace wildcards & Logging data to send
title = wildcardHandler.replaceWildcards(title, data)
logging.debug("Title : %s", title)
text = wildcardHandler.replaceWildcards(text, data)
logging.debug("Text : %s", text)
if typ == "FMS":
vehicle = wildcardHandler.replaceWildcards(vehicle, data)
logging.debug("Vehicle : %s", vehicle)
elif typ == "POC":
ric = wildcardHandler.replaceWildcards(ric, data)
logging.debug("RIC : %s", ric)
elif typ == "ZVEI":
zvei_id = wildcardHandler.replaceWildcards(zvei_id, data)
logging.debug("ZVEI_ID : %s", zvei_id)
else:
logging.info("No wildcards to replace and no Typ selected!")
# check priority value
if (priority != 'false') and (priority != 'true'):
logging.info("No Priority set for type '%s'! Skipping Divera-Alarm!", typ)
return
# Check FMS
if typ == "FMS":
if (vehicle == ''):
logging.info("No Vehicle set!")
# Check POC
elif typ == "POC":
if (ric == ''):
logging.info("No RIC set!")
# Check ZVEI
elif typ == "ZVEI":
if (zvei_id == ''):
logging.info("No ZVEI_ID set!")
else:
logging.info("No ZVEI, FMS or POC alarm")
# start connection to Divera
if typ == "FMS":
# start the connection FMS
conn = httplib.HTTPSConnection("www.divera247.com:443")
conn.request("GET", "/api/fms",
urllib.urlencode({
"accesskey": globalVars.config.get("Divera", "accesskey"),
"vehicle_ric": vehicle,
"status_id": data["status"],
"status_note": data["directionText"],
"title": title,
"text": text,
"priority": priority,
}))
elif typ == "ZVEI":
# start connection ZVEI; zvei_id in Divera is alarm-RIC!
conn = httplib.HTTPSConnection("www.divera247.com:443")
conn.request("GET", "/api/alarm",
urllib.urlencode({
"accesskey": globalVars.config.get("Divera", "accesskey"),
"title": title,
"ric": zvei_id,
"text": text,
"priority": priority,
}))
elif typ == "POC":
# start connection POC
conn = httplib.HTTPSConnection("www.divera247.com:443")
conn.request("GET", "/api/alarm",
urllib.urlencode({
"accesskey": globalVars.config.get("Divera", "accesskey"),
"title": title,
"ric": ric,
"text": text,
"priority": priority,
}))
else:
loggin.debug("No Type is set", exc_info=True)
return
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

@ -13,8 +13,6 @@ import logging # Global logger
import hmac, hashlib import hmac, hashlib
import json, requests import json, requests
from collections import OrderedDict
from includes import globalVars # Global variables from includes import globalVars # Global variables
#from includes.helper import timeHandler #from includes.helper import timeHandler
@ -125,7 +123,6 @@ def run(typ,freq,data):
} }
alarmData = json.dumps(alarmData) alarmData = json.dumps(alarmData)
logging.debug(alarmData) logging.debug(alarmData)
alarmHeaders = { alarmHeaders = {
@ -136,29 +133,10 @@ def run(typ,freq,data):
"hmac": hmac.new(webApiKey, webApiToken + selectiveCallCode + accessToken + alarmData, digestmod=hashlib.sha256).hexdigest() "hmac": hmac.new(webApiKey, webApiToken + selectiveCallCode + accessToken + alarmData, digestmod=hashlib.sha256).hexdigest()
} }
logging.debug(alarmHeaders)
alarmHeadersOrdered=OrderedDict()
alarmHeadersOrdered['webApiToken']=webApiToken
alarmHeadersOrdered['accessToken']=accessToken
alarmHeadersOrdered['selectiveCallCode']=selectiveCallCode
alarmHeadersOrdered['hmac']=hmac.new(webApiKey, webApiToken + selectiveCallCode + accessToken + alarmData, digestmod=hashlib.sha256).hexdigest()
logging.debug(alarmHeadersOrdered)
if globalVars.config.get("FFAgent", "live") == "1": if globalVars.config.get("FFAgent", "live") == "1":
s = requests.Session() r = requests.post(url, data=alarmData, headers=headers, verify=serverCertFile, cert=(clientCertFile, clientCertPass))
s.headers = OrderedDict([('Content-Type', 'application/json')])
logging.debug(s.headers)
r = s.post(url, data=alarmData, headers=alarmHeadersOrdered, verify=serverCertFile, cert=(clientCertFile, clientCertPass))
else: else:
s = requests.Session() r = requests.post(url, data=alarmData, headers=alarmHeaders, verify=serverCertFile)
s.headers = OrderedDict([('Content-Type', 'application/json')])
logging.debug(s.headers)
r = s.post(url, data=alarmData, headers=alarmHeadersOrdered, verify=serverCertFile)
logging.debug(r.request.headers)
except: except:
logging.error("cannot send FFAgent request") logging.error("cannot send FFAgent request")

View file

@ -91,7 +91,7 @@ def run(typ,freq,data):
# Connect to MySQL # Connect to MySQL
# #
logging.debug("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 = 'utf8mb4', collation = 'utf8mb4_general_ci') connection = mysql.connector.connect(host = globalVars.config.get("MySQL","dbserver"), user = globalVars.config.get("MySQL","dbuser"), passwd = globalVars.config.get("MySQL","dbpassword"), db = globalVars.config.get("MySQL","database"), charset='utf8')
cursor = connection.cursor() cursor = connection.cursor()
except: except:
logging.error("cannot connect to MySQL") logging.error("cannot connect to MySQL")
@ -104,21 +104,18 @@ def run(typ,freq,data):
logging.debug("Insert %s", typ) logging.debug("Insert %s", typ)
if typ == "FMS": 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": 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": elif typ == "POC":
if isSignal(data["ric"]): if isSignal(data["ric"]):
if globalVars.config.getint("POC","netIdent_history"): cursor.execute("UPDATE "+globalVars.config.get("MySQL","tableSIG")+" SET time = NOW() WHERE ric = "+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: 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: 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: else:
logging.warning("Invalid Typ: %s", typ) 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_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */; /*!40101 SET NAMES utf8 */;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -25,8 +25,8 @@ SET time_zone = "+00:00";
-- Datenbank anlegen `boswatch` -- Datenbank anlegen `boswatch`
-- --
CREATE DATABASE IF NOT EXISTS 'boswatch' DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS boswatch;
USE 'boswatch'; USE boswatch;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS `bos_fms` (
`tsi` VARCHAR(3) NOT NULL, `tsi` VARCHAR(3) NOT NULL,
`description` TEXT NOT NULL, `description` TEXT NOT NULL,
PRIMARY KEY (`ID`) PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; ) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -70,9 +70,8 @@ CREATE TABLE IF NOT EXISTS `bos_pocsag` (
`msg` TEXT NOT NULL, `msg` TEXT NOT NULL,
`bitrate` INT(4) NOT NULL, `bitrate` INT(4) NOT NULL,
`description` TEXT NOT NULL, `description` TEXT NOT NULL,
PRIMARY KEY (`ID`), PRIMARY KEY (`ID`)
KEY `POCSAG_RIC_IDX` (`ric`) ) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;
-- rename old columns including little error-prevention -- rename old columns including little error-prevention
#ALTER IGNORE TABLE `bos_pocsag` change `funktion` `function` INT(1); #ALTER IGNORE TABLE `bos_pocsag` change `funktion` `function` INT(1);
@ -90,7 +89,7 @@ CREATE TABLE IF NOT EXISTS `bos_zvei` (
`zvei` VARCHAR(5) NOT NULL DEFAULT '0', `zvei` VARCHAR(5) NOT NULL DEFAULT '0',
`description` TEXT NOT NULL, `description` TEXT NOT NULL,
PRIMARY KEY (`ID`) PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; ) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -103,7 +102,45 @@ CREATE TABLE IF NOT EXISTS `bos_signal` (
`time` DATETIME NOT NULL, `time` DATETIME NOT NULL,
`ric` VARCHAR(7) NOT NULL DEFAULT '0', `ric` VARCHAR(7) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`) PRIMARY KEY (`ID`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1; ) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
-- --------------------------------------------------------
--
-- Schedule für Tabelle `bos_pocsag`
--
CREATE EVENT IF NOT EXISTS `Delete POCSAG Entries > 3 Months`
ON SCHEDULE EVERY 1 DAY
STARTS '2016-01-01 00:00:00'
ON COMPLETION PRESERVE ENABLE
DO
DELETE FROM bos_pocsag WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
-- --------------------------------------------------------
--
-- Schedule für Tabelle `bos_fms`
--
CREATE EVENT IF NOT EXISTS `Delete FMS Entries > 3 Months`
ON SCHEDULE EVERY 1 DAY
STARTS '2016-01-01 00:00:00'
ON COMPLETION PRESERVE ENABLE
DO
DELETE FROM bos_fms WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
-- --------------------------------------------------------
--
-- Schedule für Tabelle `bos_zvei`
--
CREATE EVENT IF NOT EXISTS `Delete ZVEI Entries > 3 Months`
ON SCHEDULE EVERY 1 DAY
STARTS '2016-01-01 00:00:00'
ON COMPLETION PRESERVE ENABLE
DO
DELETE FROM bos_zvei WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

View file

@ -1,40 +0,0 @@
-- Cleanup-routines for boswatch-tables
use boswatch;
-- --------------------------------------------------------
--
-- Schedule für Tabelle `bos_pocsag`
--
CREATE EVENT IF NOT EXISTS `Delete POCSAG Entries > 3 Months`
ON SCHEDULE EVERY 1 DAY
STARTS '2016-01-01 00:00:00'
ON COMPLETION PRESERVE ENABLE
DO
DELETE FROM bos_pocsag WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
-- --------------------------------------------------------
--
-- Schedule für Tabelle `bos_fms`
--
CREATE EVENT IF NOT EXISTS `Delete FMS Entries > 3 Months`
ON SCHEDULE EVERY 1 DAY
STARTS '2016-01-01 00:00:00'
ON COMPLETION PRESERVE ENABLE
DO
DELETE FROM bos_fms WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
-- --------------------------------------------------------
--
-- Schedule für Tabelle `bos_zvei`
--
CREATE EVENT IF NOT EXISTS `Delete ZVEI Entries > 3 Months`
ON SCHEDULE EVERY 1 DAY
STARTS '2016-01-01 00:00:00'
ON COMPLETION PRESERVE ENABLE
DO
DELETE FROM bos_zvei WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);

View file

@ -10,14 +10,12 @@ Pushover-Plugin to send FMS-, ZVEI- and POCSAG - messages to Pushover Clients
""" """
import logging # Global logger import logging # Global logger
import httplib # for the HTTP request import httplib #for the HTTP request
import urllib import urllib
from includes import globalVars # Global variables from includes import globalVars # Global variables
# from includes.helper import timeHandler #from includes.helper import timeHandler
from includes.helper import configHandler from includes.helper import configHandler
from includes.helper import wildcardHandler
## ##
# #
@ -42,7 +40,7 @@ def onLoad():
# Main function of Pushover-plugin # Main function of Pushover-plugin
# will be called by the alarmHandler # will be called by the alarmHandler
# #
def run(typ, freq, data): def run(typ,freq,data):
""" """
This function is the implementation of the Pushover-Plugin. This function is the implementation of the Pushover-Plugin.
It will send the data to Pushover API It will send the data to Pushover API
@ -59,45 +57,14 @@ def run(typ, freq, data):
@return: nothing @return: nothing
""" """
try: try:
if configHandler.checkConfig("Pushover"): # read and debug the config if configHandler.checkConfig("Pushover"): #read and debug the config
if typ == "FMS":
#
# building message for FMS
#
message = globalVars.config.get("Pushover", "fms_message")
title = globalVars.config.get("Pushover", "fms_title")
priority = globalVars.config.get("Pushover", "fms_prio")
logging.debug("Sending message: %s", message)
elif typ == "ZVEI":
#
# building message for ZVEI
#
if globalVars.config.get("Pushover", "zvei_sep_prio") == '1':
if data["zvei"] in globalVars.config.get("Pushover", "zvei_prio2"):
priority = '2'
elif data["zvei"] in globalVars.config.get("Pushover","zvei_prio1"):
priority = '1'
elif data["zvei"] in globalVars.config.get("Pushover","zvei_prio0"):
priority = '0'
else:
priority = '-1'
else:
priority = globalVars.config.get("Pushover","zvei_std_prio")
message = globalVars.config.get("Pushover", "zvei_message")
title = globalVars.config.get("Pushover", "zvei_title")
logging.debug("Sending message: %s", message)
elif typ == "POC":
try:
# #
# Pushover-Request # Pushover-Request
# #
logging.debug("send Pushover for %s", typ) logging.debug("send Pushover %s", typ)
if globalVars.config.get("Pushover", "poc_spec_ric") == '0':
if data["function"] == '1': if data["function"] == '1':
priority = globalVars.config.get("Pushover", "SubA") priority = globalVars.config.get("Pushover", "SubA")
elif data["function"] == '2': elif data["function"] == '2':
@ -108,61 +75,36 @@ def run(typ, freq, data):
priority = globalVars.config.get("Pushover", "SubD") priority = globalVars.config.get("Pushover", "SubD")
else: else:
priority = 0 priority = 0
else:
if data["ric"] in globalVars.config.get("Pushover", "poc_prio2"):
priority = 2
elif data["ric"] in globalVars.config.get("Pushover","poc_prio1"):
priority = 1
elif data["ric"] in globalVars.config.get("Pushover","poc_prio0"):
priority = 0
else:
priority = -1
message = globalVars.config.get("Pushover", "poc_message")
title = globalVars.config.get("Pushover", "poc_title")
else:
logging.warning("Invalid type: %s", typ)
try:
# 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") conn = httplib.HTTPSConnection("api.pushover.net:443")
conn.request("POST", "/1/messages.json", conn.request("POST", "/1/messages.json",
urllib.urlencode({ urllib.urlencode({
"token": globalVars.config.get("Pushover", "api_key"), "token": globalVars.config.get("Pushover", "api_key"),
"user": globalVars.config.get("Pushover", "user_key"), "user": globalVars.config.get("Pushover", "user_key"),
"message": message, "message": "<b>"+data["description"]+"</b><br>"+data["msg"].replace(";", "<br>"),
"html": globalVars.config.get("Pushover", "html"), "html": globalVars.config.get("Pushover", "html"),
"title": title, "title": globalVars.config.get("Pushover", "title"),
"sound": sound,
"priority": priority, "priority": priority,
"retry": globalVars.config.get("Pushover", "retry"), "retry": globalVars.config.get("Pushover", "retry"),
"expire": globalVars.config.get("Pushover", "expire") "expire": globalVars.config.get("Pushover", "expire")
}), {"Content-type": "application/x-www-form-urlencoded"}) }),{"Content-type": "application/x-www-form-urlencoded"})
except: except:
logging.error("cannot send Pushover request") logging.error("cannot send Pushover request")
logging.debug("cannot send Pushover request", exc_info=True) logging.debug("cannot send Pushover request", exc_info=True)
return return
else:
try: try:
# #
# check Pushover-Response # check Pushover-Response
# #
response = conn.getresponse() response = conn.getresponse()
if str(response.status) == "200": # Check Pushover Response and print a Log or Error if str(response.status) == "200": #Check Pushover Response and print a Log or Error
logging.debug("Pushover response: %s - %s", str(response.status), str(response.reason)) logging.debug("Pushover response: %s - %s" , str(response.status), str(response.reason))
else: else:
logging.warning("Pushover response: %s - %s", str(response.status), str(response.reason)) logging.warning("Pushover response: %s - %s" , str(response.status), str(response.reason))
except: # otherwise except: #otherwise
logging.error("cannot get Pushover response") logging.error("cannot get Pushover response")
logging.debug("cannot get Pushover response", exc_info=True) logging.debug("cannot get Pushover response", exc_info=True)
return return

View file

@ -16,16 +16,6 @@ This `.run()` routine is called every time an alarm comes in
Here are the information from BOSWatch available. See section `5. Process the data from BOSWatch` Here are the information from BOSWatch available. See section `5. Process the data from BOSWatch`
#### 1.4 Requirements
Add all required (which need to be installed separately) python packages to a requirements.txt in the plugin directory so that the user can simply install all requirements for this plugin.
For examples look at [the Telegram plugin](Telegram/requirements.txt)
##### 1.4.1 Requirement installation
To install the packages from the requirements.txt run
`pip install -r /path/to/plugin/directory/requirements.txt`
Or because for the current version (2.5) Python2 is required
`pip2 install -r /path/to/plungin/directory/requirements.txt` will work for sure
## 2. Use Global Logging ## 2. Use Global Logging
#### 2.1 Init and Use #### 2.1 Init and Use
@ -203,7 +193,6 @@ In the data map are the folowing informations:
- ric - ric
- function - function
- functionChar - functionChar
- ricFuncChar
- msg - msg
- bitrate - bitrate
- description - description

View file

@ -59,20 +59,6 @@ def run(typ,freq,data):
try: try:
if configHandler.checkConfig("Sms77"): #read and debug the config if configHandler.checkConfig("Sms77"): #read and debug the config
# create an empty message an fill it with the required information
message = "Alarm"
if typ == "FMS":
logging.debug("FMS detected, building message")
message = data["description"]+"<br>"+data["status"]
elif typ == "ZVEI":
logging.debug("ZVEI detected, building message")
message = data["zvei"]+" - "+data["description"]
elif typ == "POC":
logging.debug("POC detected, building message")
message = data["description"]+"<br>"+data["msg"].replace(";", "<br>")
else:
logging.warning("Invalid typ - use empty message")
try: try:
# #
@ -80,15 +66,15 @@ def run(typ,freq,data):
# #
logging.debug("send Sms77 %s", typ) logging.debug("send Sms77 %s", typ)
conn = httplib.HTTPSConnection("gateway.sms77.io",443) conn = httplib.HTTPSConnection("gateway.sms77.de:443")
conn.request("POST", "/api/sms", conn.request("POST", "",
urllib.urlencode({ urllib.urlencode({
"u": globalVars.config.get("Sms77", "user"), "u": globalVars.config.get("Sms77", "user"),
"p": globalVars.config.get("Sms77", "password"), "p": globalVars.config.get("Sms77", "password"),
"to": globalVars.config.get("Sms77", "to"), "to": globalVars.config.get("Sms77", "to"),
"from": globalVars.config.get("Sms77", "from"), "from": globalVars.config.get("Sms77", "from"),
"type": globalVars.config.get("Sms77", "type"), "type": globalVars.config.get("Sms77", "type"),
"text": message "text": data["description"]+"<br>"+data["msg"].replace(";", "<br>")
}),{"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}) }),{"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"})
except: except:

View file

@ -4,29 +4,26 @@
""" """
Plugin to send FMS-, ZVEI- and POCSAG-messages via Telegram Plugin to send FMS-, ZVEI- and POCSAG-messages via Telegram
@author: Peter Laemmle @author: Peter Laemmle
@requires: Telegram BOT token, Telegram chat ID, library python-telegram-bot and optional requests and json @requires: Telegram BOT token, Telegram chat ID, library python-telegram-bot and optional googlemaps
""" """
# #
# Imports # Imports
# #
import logging # Global logger import logging # Global logger
import telegram import urllib, telegram, googlemaps
from telegram.error import (TelegramError, Unauthorized, BadRequest, NetworkError) from telegram.error import (TelegramError, Unauthorized, BadRequest, NetworkError)
from includes import globalVars # Global variables from includes import globalVars # Global variables
if globalVars.config.get("Telegram","RICforLocationAPIKey"):
import requests, json
# Helper function, uncomment to use # Helper function, uncomment to use
from includes.helper import wildcardHandler
from includes.helper import configHandler from includes.helper import configHandler
from includes.helper import timeHandler
# local variables # local variables
BOTTokenAPIKey = None BOTTokenAPIKey = None
BOTChatIDAPIKey = None BOTChatIDAPIKey = None
RICforLocationAPIKey = None RICforLocationAPIKey = None
GoogleAPIKey = None GoogleAPIKey = None
RoutingOrigin = None
## ##
# #
@ -44,14 +41,12 @@ def onLoad():
global BOTChatIDAPIKey global BOTChatIDAPIKey
global RICforLocationAPIKey global RICforLocationAPIKey
global GoogleAPIKey global GoogleAPIKey
global RoutingOrigin
configHandler.checkConfig("Telegram") configHandler.checkConfig("Telegram")
BOTTokenAPIKey = globalVars.config.get("Telegram","BOTTokenAPIKey") BOTTokenAPIKey = globalVars.config.get("Telegram","BOTTokenAPIKey")
BOTChatIDAPIKey = globalVars.config.get("Telegram","BOTChatIDAPIKey") BOTChatIDAPIKey = globalVars.config.get("Telegram","BOTChatIDAPIKey")
RICforLocationAPIKey = globalVars.config.get("Telegram","RICforLocationAPIKey") RICforLocationAPIKey = globalVars.config.get("Telegram","RICforLocationAPIKey")
GoogleAPIKey = globalVars.config.get("Telegram","GoogleAPIKey") GoogleAPIKey = globalVars.config.get("Telegram","GoogleAPIKey")
RoutingOrigin = globalVars.config.get("Telegram","RoutingOrigin")
return return
@ -83,38 +78,29 @@ def run(typ,freq,data):
try: try:
########## User Plugin CODE ########## ########## User Plugin CODE ##########
try: try:
if typ in ("POC", "FMS", "ZVEI"): if typ == "POC":
logging.debug("Read format and compose output for %s-message" % typ) logging.debug("Compose output from POCSAG-message")
# compose message content # compose message content
text = globalVars.config.get("Telegram", "%s_message" % typ) output = timeHandler.curtime()+"\n"+data["ric"]+"("+data["functionChar"]+")\n"+data["description"]+"\n"+data["msg"]
text = wildcardHandler.replaceWildcards(text, data)
# Initiate Telegram Bot # Initiate Telegram Bot
logging.debug("Initiate Telegram BOT") logging.debug("Initiate Telegram BOT")
bot = telegram.Bot(token='%s' % BOTTokenAPIKey) bot = telegram.Bot(token='%s' % BOTTokenAPIKey)
# Send message to chat via Telegram BOT API # Send message to chat via Telegram BOT API
logging.debug("Send message to chat via Telegram BOT API") logging.debug("Send message to chat via Telegram BOT API")
bot.sendMessage('%s' % BOTChatIDAPIKey, text, parse_mode=telegram.ParseMode.HTML) bot.sendMessage('%s' % BOTChatIDAPIKey, output)
# Generate location information only for specific RIC # Generate location information only for specific RIC
if typ == "POC" and data["ric"] == RICforLocationAPIKey: if data["ric"] == RICforLocationAPIKey:
# Generate map # Generate map
logging.debug("Extract address from POCSAG message") logging.debug("Extract address from POCSAG message")
address = "+".join(data["msg"].split(')')[0].split('/',1)[1].replace('(',' ').split()) address = "+".join(data["msg"].split(')')[0].split('/',1)[1].replace('(',' ').split())
# Retrieve directions using Google API
logging.debug("Retrieve polylines from Directions API")
url = "".join(["https://maps.googleapis.com/maps/api/directions/json?origin=",
RoutingOrigin, "&destination=", address, "&mode=driving&key=", GoogleAPIKey])
response = json.loads(requests.get(url).content.decode('utf-8'))
logging.debug("Directions API return status: %s" % response['status'])
# Retrieve static maps using Google API
logging.debug("Retrieve maps from Google") logging.debug("Retrieve maps from Google")
url = "".join(["https://maps.googleapis.com/maps/api/staticmap?&size=480x640&maptype=roadmap&path=enc:", url = "+".join(["http://maps.googleapis.com/maps/api/staticmap?markers=", address, "&size=480x640&maptype=roadmap&zoom=16&key=", GoogleAPIKey])
response['routes'][0]['overview_polyline']['points'], "&language=de&key=", GoogleAPIKey]) urllib.urlretrieve(url, "overview_map.png")
with open("overview_map.png", "wb") as img: img.write(requests.get(url).content) url = "+".join(["http://maps.googleapis.com/maps/api/staticmap?markers=", address, "&size=240x320&scale=2&maptype=hybrid&zoom=17&key=", GoogleAPIKey])
url = "".join(["https://maps.googleapis.com/maps/api/staticmap?markers=", urllib.urlretrieve(url, "detail_map.png")
address, "&size=240x320&scale=2&maptype=hybrid&zoom=17&language=de&key=", GoogleAPIKey])
with open("detail_map.png", "wb") as img: img.write(requests.get(url).content)
# Send message and map with Telegram # Send message and map with Telegram
logging.debug("Send message and maps via Telegram BOT") logging.debug("Send message and maps via Telegram BOT")
@ -123,15 +109,32 @@ def run(typ,freq,data):
# Geocoding of address # Geocoding of address
logging.debug("Geocode address") logging.debug("Geocode address")
url = "".join(["https://maps.googleapis.com/maps/api/geocode/json?address=", gcode = googlemaps.Client(key='%s' % GoogleAPIKey)
address, "&language=de&key=", GoogleAPIKey]) gcode_result = gcode.geocode(address)
gcode_result = json.loads(requests.get(url).content)
logging.debug("Geocoding API return status: %s" % gcode_result['status'])
logging.debug("Send location via Telegram BOT API") logging.debug("Send location via Telegram BOT API")
bot.sendLocation('%s' % BOTChatIDAPIKey, bot.sendLocation('%s' % BOTChatIDAPIKey, gcode_result[0]['geometry']['location']['lat'], gcode_result[0]['geometry']['location']['lng'], disable_notification='true')
gcode_result[results][0]['geometry']['location']['lat'], elif typ == "FMS":
gcode_result[results][0]['geometry']['location']['lng'], logging.debug("Compose output from FMS-message")
disable_notification='true') # compose message content
output = timeHandler.curtime()+"\n"+data["fms"]+"\n"+data["description"]+"\n"+data["status"]
# Initiate Telegram Bot
logging.debug("Initiate Telegram BOT")
bot = telegram.Bot(token='%s' % BOTTokenAPIKey)
# Send message to chat via Telegram BOT API
logging.debug("Send message to chat via Telegram BOT API")
bot.sendMessage('%s' % BOTChatIDAPIKey, output)
elif typ == "ZVEI":
logging.debug("Compose output from ZVEI-message")
# compose message content
output = timeHandler.curtime()+"\n"+data["zvei"]+"\n"+data["description"]
# Initiate Telegram Bot
logging.debug("Initiate Telegram BOT")
bot = telegram.Bot(token='%s' % BOTTokenAPIKey)
# Send message to chat via Telegram BOT API
logging.debug("Send message to chat via Telegram BOT API")
bot.sendMessage('%s' % BOTChatIDAPIKey, output)
else: else:
logging.warning("Invalid Typ: %s", typ) logging.warning("Invalid Typ: %s", typ)
except Unauthorized: except Unauthorized:

View file

@ -1,2 +0,0 @@
python-telegram-bot
requests

View file

@ -13,7 +13,6 @@ import logging # Global logger
import smtplib #for the SMTP client import smtplib #for the SMTP client
from email.mime.text import MIMEText # Import the email modules we'll need from email.mime.text import MIMEText # Import the email modules we'll need
from email.header import Header # Import the email modules we'll need
from email.utils import formatdate # need for confirm to RFC2822 standard from email.utils import formatdate # need for confirm to RFC2822 standard
from email.utils import make_msgid # need for confirm to RFC2822 standard from email.utils import make_msgid # need for confirm to RFC2822 standard
@ -62,7 +61,7 @@ def doSendmail(server, subject, mailtext):
msg = MIMEText(mailtext, 'plain', 'UTF-8') msg = MIMEText(mailtext, 'plain', 'UTF-8')
msg['From'] = globalVars.config.get("eMail", "from") msg['From'] = globalVars.config.get("eMail", "from")
msg['To'] = globalVars.config.get("eMail", "to") msg['To'] = globalVars.config.get("eMail", "to")
msg['Subject'] = Header(subject, 'UTF-8') msg['Subject'] = subject
msg['Date'] = formatdate() msg['Date'] = formatdate()
msg['Message-Id'] = make_msgid() msg['Message-Id'] = make_msgid()
msg['Priority'] = globalVars.config.get("eMail", "priority") msg['Priority'] = globalVars.config.get("eMail", "priority")
@ -138,7 +137,7 @@ def run(typ,freq,data):
# read mailtext-structure from config.ini # read mailtext-structure from config.ini
mailtext = globalVars.config.get("eMail", "fms_message") mailtext = globalVars.config.get("eMail", "fms_message")
# replace wildcards with helper function # replace wildcards with helper function
mailtext = wildcardHandler.replaceWildcards(mailtext, data) mailtext = wildcardHandler.replaceWildcards(mailtext, data, lineBrakeAllowed=True)
# send eMail # send eMail
doSendmail(server, subject, mailtext) doSendmail(server, subject, mailtext)
@ -158,7 +157,7 @@ def run(typ,freq,data):
# read mailtext-structure from config.ini # read mailtext-structure from config.ini
mailtext = globalVars.config.get("eMail", "zvei_message") mailtext = globalVars.config.get("eMail", "zvei_message")
# replace wildcards with helper function # replace wildcards with helper function
mailtext = wildcardHandler.replaceWildcards(mailtext, data) mailtext = wildcardHandler.replaceWildcards(mailtext, data, lineBrakeAllowed=True)
# send eMail # send eMail
doSendmail(server, subject, mailtext) doSendmail(server, subject, mailtext)
@ -178,7 +177,7 @@ def run(typ,freq,data):
# read mailtext-structure from config.ini # read mailtext-structure from config.ini
mailtext = globalVars.config.get("eMail", "poc_message") mailtext = globalVars.config.get("eMail", "poc_message")
# replace wildcards with helper function # replace wildcards with helper function
mailtext = wildcardHandler.replaceWildcards(mailtext, data) mailtext = wildcardHandler.replaceWildcards(mailtext, data, lineBrakeAllowed=True)
# send eMail # send eMail
doSendmail(server, subject, mailtext) doSendmail(server, subject, mailtext)

View file

@ -1,113 +0,0 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Plugin for calling FHEM home automation
@author: Marco Schotthöfer
@requires: python-fhem (pip install fhem)
"""
#
# Imports
#
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
from includes.helper import wildcardHandler
import fhem
##
#
# 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
"""
try:
########## User onLoad CODE ##########
pass
########## User onLoad CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)
raise
##
#
# Main function of plugin
# will be called by the alarmHandler
#
def run(typ,freq,data):
"""
This function is the implementation of the Plugin.
If necessary the configuration hast to be set in the config.ini.
@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("fhemCmd"): #read and debug the config (let empty if no config used)
protocol = globalVars.config.get("fhemCmd", "protocol")
logging.debug("protocol: %s", protocol)
server = globalVars.config.get("fhemCmd", "server")
logging.debug("server: %s", server)
port = globalVars.config.get("fhemCmd", "port")
logging.debug("port: %s", port)
username = globalVars.config.get("fhemCmd", "username")
logging.debug("username: %s", username)
password = globalVars.config.get("fhemCmd", "password")
logging.debug("password: %s", password)
########## User Plugin CODE ##########
if typ == "FMS":
fhemCommand = globalVars.config.get("fhemCmd", "commandFMS")
elif typ == "ZVEI":
fhemCommand = globalVars.config.get("fhemCmd", "commandZVEI")
elif typ == "POC":
fhemCommand = globalVars.config.get("fhemCmd", "commandPOC")
else:
logging.warning("Invalid Typ: %s", typ)
return False
fhemCommand = wildcardHandler.replaceWildcards(fhemCommand, data)
logging.debug("fhemCommand: %s", fhemCommand)
fh = fhem.Fhem(server=server, protocol=protocol, port=port, username=username, password=password)
fh.send_cmd(fhemCommand)
del fh
########## User Plugin CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)

View file

@ -1 +0,0 @@
fhem

View file

@ -5,7 +5,7 @@
firEmergency-Plugin to dispatch ZVEI- and POCSAG - messages to firEmergency firEmergency-Plugin to dispatch ZVEI- and POCSAG - messages to firEmergency
firEmergency configuration: firEmergency configuration:
- set input to "Standartschnittstelle" at Port 5555 - set input to "FMS32" at Port 5555
@autor: Smith-fms @autor: Smith-fms

View file

@ -1,118 +0,0 @@
#!/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

View file

@ -14,7 +14,6 @@ httpRequest-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to an URL
# #
# Imports # Imports
# #
import urllib
import urllib2 import urllib2
import logging # Global logger import logging # Global logger
from includes import globalVars # Global variables from includes import globalVars # Global variables
@ -68,38 +67,31 @@ def run(typ,freq,data):
try: try:
# #
# Make a copy of the data field to not overwrite the data in it # Create URL
# Replace special characters in dataCopy Strings for URL
#
dataCopy = dict(data)
for key in dataCopy:
if isinstance(dataCopy[key], basestring):
dataCopy[key] = urllib.quote(dataCopy[key])
#
# Get URLs
# #
if typ == "FMS": if typ == "FMS":
urls = globalVars.config.get("httpRequest", "fms_url").split(",") url = globalVars.config.get("httpRequest", "fms_url") #Get URL
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
url = url.replace(" ","%20") # replace space with %20 to be a vaild http request
elif typ == "ZVEI": elif typ == "ZVEI":
urls = globalVars.config.get("httpRequest", "zvei_url").split(",") url = globalVars.config.get("httpRequest", "zvei_url") #Get URL
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
url = url.replace(" ","%20") # replace space with %20 to be a vaild http request
elif typ == "POC": elif typ == "POC":
urls = globalVars.config.get("httpRequest", "poc_url").split(",") url = globalVars.config.get("httpRequest", "poc_url") #Get URL
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
url = url.replace(" ","%20") # replace space with %20 to be a vaild http request
else: else:
logging.warning("Invalid Typ: %s", typ) logging.warning("Invalid Typ: %s", typ)
return return
# #
# replace wildcards
#
for (i, url) in enumerate(urls):
urls[i] = wildcardHandler.replaceWildcards(urls[i].strip(), dataCopy)
#
# HTTP-Request # HTTP-Request
# #
logging.debug("send %s HTTP requests", typ) logging.debug("send %s HTTP request", typ)
for url in urls:
try: try:
#resp = urllib2.urlopen(url)
urllib2.urlopen(url) urllib2.urlopen(url)
except urllib2.HTTPError as e: except urllib2.HTTPError as e:
logging.warning("HTTP response: %s", e.code) logging.warning("HTTP response: %s", e.code)

View file

@ -1,122 +0,0 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Plugin to control Philips hue lights and switches
@author: Fabian Kessler
@requires: none
"""
#
# Imports
#
import logging # Global logger
from includes import globalVars # Global variables
import json
import requests
import time
# 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
"""
try:
########## User onLoad CODE ##########
pass
########## User onLoad CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)
raise
##
#
# Main function of plugin
# will be called by the alarmHandler
#
def run(typ,freq,data):
"""
This function is the implementation of the Plugin.
If necessary the configuration hast to be set in the config.ini.
@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("hue"): #read and debug the config
#for debugging
"""logging.debug(globalVars.config.get("hue", "bridgeip"))
logging.debug(globalVars.config.get("hue", "deviceid"))
logging.debug(globalVars.config.get("hue", "apikey"))
logging.debug(globalVars.config.getint("hue", "repeat"))
logging.debug(globalVars.config.getint("hue", "timeon"))
logging.debug(globalVars.config.getint("hue", "timeoff"))
logging.debug(globalVars.config.getint("hue", "keepon"))"""
########## User Plugin CODE ##########
if typ == "FMS":
logging.warning("%s not supported", typ)
elif typ == "ZVEI":
logging.warning("%s not supported", typ)
elif typ == "POC":
#logging.warning("%s not supported", typ)
logging.debug("POC received")
bridgeip = globalVars.config.get("hue", "bridgeip")
deviceid = globalVars.config.get("hue", "deviceid")
apikey = globalVars.config.get("hue", "apikey")
repeat = globalVars.config.getint("hue", "repeat")
timeon = globalVars.config.getint("hue", "timeon")
timeoff = globalVars.config.getint("hue", "timeoff")
keepon = globalVars.config.getint("hue", "keepon")
data_on = '{"on":true}'
data_off = '{"on":false}'
url = "http://" + bridgeip + "/api/" + apikey + "/lights/" + deviceid + "/state"
logging.debug("hue REST API URL: %s", url)
#blinking
for _ in xrange(repeat):
requests.put(url, data=data_on)
logging.debug("on for %s seconds", timeon)
time.sleep(timeon)
requests.put(url, data=data_off)
logging.debug("off for %s seconds", timeoff)
time.sleep(timeoff)
if keepon > 0:
logging.debug("switch to on and wait for keepon to expire")
requests.put(url, data=data_on)
logging.debug("keep on for %s seconds", keepon)
time.sleep(keepon)
requests.put(url, data=data_off)
else:
logging.debug("switch to on and exit plugin")
requests.put(url, data=data_on)
else:
logging.warning("Invalid Typ: %s", typ)
########## User Plugin CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)

View file

@ -1,121 +0,0 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author: KS
@requires: paho-mqtt
"""
#
# Imports
#
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
import paho.mqtt.client as mqtt
import json
##
#
# 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
"""
try:
########## User onLoad CODE ##########
pass
########## User onLoad CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)
raise
##
#
# Main function of plugin
# will be called by the alarmHandler
#
def run(typ,freq,data):
"""
This function is the implementation of the Plugin.
If necessary the configuration hast to be set in the config.ini.
@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("mqtt"): #read and debug the config (let empty if no config used)
logging.debug(globalVars.config.get("mqtt", "brokeraddress"))
logging.debug(globalVars.config.get("mqtt", "topic"))
########## User Plugin CODE ##########
broker_address = globalVars.config.get("mqtt", "brokeraddress")
topic = globalVars.config.get("mqtt", "topic")
mqttClient = mqtt.Client()
if typ == "FMS":
x = {
"fms": data["fms"],
"status": data["status"],
"direction": data["direction"],
"directionText": data["directionText"],
"tsi": data["tsi"],
"description": data["description"],
"timestamp": timeHandler.curtime()
}
elif typ == "ZVEI":
x = {
"zvei": data["zvei"],
"description": data["description"],
"timestamp": timeHandler.curtime()
}
elif typ == "POC":
functionText = "%FUNCTEXT%"
functionText = wildcardHandler.replaceWildcards(functionText, data)
x = {
"ric": data["ric"],
"function": data["function"],
"functionText": functionText,
"functionChar": data["functionChar"],
"msg": data["msg"],
"bitrate": data["bitrate"],
"description": data["description"],
"timestamp": timeHandler.curtime()
}
else:
logging.warning("Invalid Typ: %s", typ)
y = json.dumps(x)
mqttClient.connect(broker_address)
mqttClient.publish(topic,y)
########## User Plugin CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)

View file

@ -1 +0,0 @@
paho-mqtt

View file

@ -0,0 +1,304 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
notifyMyAndroid-Plugin to dispatch FMS-, ZVEI- and POCSAG-messages via UDP/TCP
@author: Jens Herrmann
@requires: notifyMyAndroid-Configuration has to be set in the config.ini
"""
import logging # Global logger
import csv # for loading the APIKeys
from includes import globalVars # Global variables
from includes.helper import configHandler
from includes.helper import timeHandler
from includes.helper import stringConverter
from includes.pynma import pynma
# local variables
application = "BOSWatch"
APIKey = None
remainingMsgs = None
usecsv = False
# data structures: xAPIKeyList[id][i] = (APIKey, priority, eventPrefix)
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
##
#
# 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
"""
# local variables
global application
global APIKey
global usecsv
# load config:
configHandler.checkConfig("notifyMyAndroid")
application = stringConverter.convertToUnicode(globalVars.config.get("notifyMyAndroid","appName"))
usecsv = globalVars.config.getboolean("notifyMyAndroid","usecsv")
# if no csv should use, we take the APIKey directly
if usecsv == False:
APIKey = globalVars.config.get("notifyMyAndroid","APIKey")
else:
# import the csv-file
try:
logging.debug("-- loading nma.csv")
with open(globalVars.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'], row['eventPrefix']))
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'], row['eventPrefix']))
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'], row['eventPrefix']))
except:
# skip entry in case of an exception
logging.debug("error in shifting...", exc_info=True)
# 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
##
#
# Main function of jsonSocket-plugin
# will be called by the alarmHandler
#
def run(typ,freq,data):
"""
This function is the implementation of the notifyMyAndroid-Plugin.
The configuration is set in the config.ini.
@type typ: string (FMS|ZVEI|POC)
@param typ: Typ of the dataset for sending via UDP/TCP
@type data: map of data (structure see readme.md in plugin folder)
@param data: Contains the parameter for dispatch to UDP.
@type freq: string
@keyword freq: frequency of the SDR Stick
@requires: notifyMyAndroid-Configuration has to be set in the config.ini
@return: nothing
"""
# local variables
global fmsAPIKeyList
global zveiAPIKeyList
global pocAPIKeyList
try:
try:
#
# initialize to pyNMA
#
nma = pynma.PyNMA()
except:
logging.error("cannot initialize pyNMA")
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:
# build event and msg
# pyNMA expect strings are not in UTF-8
event = stringConverter.convertToUnicode(data['description'])
msg = timeHandler.getDateTime(data['timestamp'])
if ("POC" in typ) and (len(data['msg']) > 0):
msg += "\n" + data['msg']
msg = stringConverter.convertToUnicode(msg)
# if not using csv-import, all is simple...
if usecsv == False:
response = nma.pushWithAPIKey(APIKey, application, event, msg, priority=globalVars.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, xEventPrefix)
for i in range(len(fmsAPIKeyList[xID])):
xEvent = event
(xAPIKey, xPriority, xEventPrefix) = fmsAPIKeyList[xID][i]
if len(xEventPrefix) > 0:
xEvent = xEventPrefix + ": " + xEvent
response = nma.pushWithAPIKey(xAPIKey, application, xEvent, 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, xEventPrefix)
for i in range(len(zveiAPIKeyList[xID])):
xEvent = event
(xAPIKey, xPriority, xEventPrefix) = zveiAPIKeyList[xID][i]
if len(xEventPrefix) > 0:
xEvent = xEventPrefix + ": " + xEvent
response = nma.pushWithAPIKey(xAPIKey, application, xEvent, 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, xEventPrefix)
for i in range(len(pocAPIKeyList[xID])):
xEvent = event
(xAPIKey, xPriority, xEventPrefix) = pocAPIKeyList[xID][i]
if len(xEventPrefix) > 0:
xEvent = xEventPrefix + ": " + xEvent
response = nma.pushWithAPIKey(xAPIKey, application, xEvent, 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, xEventPrefix)
for i in range(len(pocAPIKeyList[xID])):
xEvent = event
(xAPIKey, xPriority, xEventPrefix) = pocAPIKeyList[xID][i]
if len(xEventPrefix) > 0:
xEvent = xEventPrefix + ": " + xEvent
response = nma.pushWithAPIKey(xAPIKey, application, xEvent, msg, priority=xPriority)
checkResponse(response, xAPIKey)
except KeyError:
# nothing found
pass
# 3. lets look for ric prefixes in pocAPIKeyList
for prefixLength in reversed(range(6)):
ricPrefix = data['ric'][:prefixLength]
#fill the ric with stars
ricPrefix = ricPrefix.ljust(8,'*')
try:
xID = ricPrefix
# data structure: pocAPIKeyList[xID][i] = (xAPIKey, xPriority, xEventPrefix)
for i in range(len(pocAPIKeyList[xID])):
xEvent = event
(xAPIKey, xPriority, xEventPrefix) = pocAPIKeyList[xID][i]
if len(xEventPrefix) > 0:
xEvent = xEventPrefix + ": " + xEvent
response = nma.pushWithAPIKey(xAPIKey, application, xEvent, 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:
logging.warning("Invalid Typ: %s", typ)
except:
# something very mysterious
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)

View file

@ -1 +0,0 @@
yowsup2

View file

@ -1,24 +1,3 @@
### Use BOSWatch as service ###
Old description below
We assume that BOSWatch is installed to /opt/boswatch! Otherwise you need to adapt all the pathes in this description and in the service-file itself.
#### Adapt the script
Enter the frequency and the decoder(s) you want to use in line 7; you can add more specific switches if you need to
### Install the service
1. Use the install-script install_service.sh as sudo: `sudo bash install_service.sh` (self explaining)
OR
1. Copy the file to /etc/systemd/system: sudo cp /opt/boswatch/service/boswatch.service /etc/systemd/system/
2. Enable the service: sudo systemctl enable boswatch.service
3. Start the service: sudo systemctl start boswatch.service
4. Check the status: sudo systemctl status boswatch.service
---
### Start BOSWatch as a daemon ### Start BOSWatch as a daemon
##### Changing the init script ##### Changing the init script

View file

@ -1,11 +0,0 @@
[Unit]
Description=BOSWatch
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python /opt/boswatch/boswatch.py -f 123.45M -a POC512
Restart=on-abort
[Install]
WantedBy=multi-user.target

View file

@ -11,7 +11,7 @@
### END INIT INFO ### END INIT INFO
# Change the next 3 lines to suit where you install your script and what you want to call it # Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/opt/boswatch DIR=/opt/boswatch/BOSWatch
DAEMON=$DIR/boswatch.py DAEMON=$DIR/boswatch.py
DAEMON_NAME=boswatch DAEMON_NAME=boswatch

View file

@ -1,52 +0,0 @@
#!/bin/bash
# Tiny script to install BOSWatch-service via systemctl
# Just a few simple steps are required to (un)register your service
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root!" 1>&2
exit 1
fi
read -p"Do you want to install (i) or remove (r) the service? " action
if [ "$action" == "i" ]; then
# 1 Check whether the right data are in the service-file
read -p"Did you adapt the file boswatch.service (y/n)? " response
if [ "$response" == "y" ]; then
# 2 Copy the file
cp boswatch.service /etc/systemd/system
# 3 Enable the service and check status
systemctl enable boswatch.service
systemctl is-enabled boswatch.service
# 4 fire it up
systemctl start boswatch.service
# 5 post the status
systemctl status boswatch.service
elif [ "$response" == "n" ]; then
echo "Please adapt your personal boswatch.service-file"
exit 1
else
echo "Invalid input - please try again"
exit 1
fi
elif [ "$action" == "r" ]; then # we want to remove the service
# stop it...
systemctl stop boswatch.service
# disable it
systemctl disable boswatch.service
# and remove it
rm /etc/systemd/system/boswatch.service
echo "BosWatch service removed"
else # error handling
echo "Invalid input - please try again"
exit 1
fi